Skip to content
第 238 / 250 章架构⏱ 12 分钟阅读

第 238 章:审计与合规实战

学习目标

  • 审计日志设计
  • 满足等保 / GDPR / PCI-DSS
  • 应急响应流程
  • 安全合规检查

一、合规体系总览

二、审计日志设计

2.1 审计要素

java
@Data
@Entity
@Table(name = "audit_log")
public class AuditLog {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String service;          // 服务名

    @Column(nullable = false)
    private String action;           // 操作类型

    @Column(nullable = false)
    private String resource;         // 操作资源

    @Column(nullable = false)
    private String userId;           // 用户 ID

    @Column(nullable = false)
    private String ip;               // IP

    @Column(columnDefinition = "TEXT")
    private String params;           // 参数(脱敏后)

    @Column(nullable = false)
    private String status;           // SUCCESS / FAILURE

    @Column(length = 4000)
    private String errorMessage;     // 异常信息

    @Column(nullable = false)
    private Instant timestamp;       // 时间
}

2.2 审计 Facade

java
@Component
public class AuditLogger {

    @Autowired
    private AuditLogRepository repo;
    @Autowired
    private ObjectMapper mapper;
    @Autowired
    private DesensitizationService desens;

    public void log(String action, String resource, Object params, Throwable error) {
        try {
            AuditLog log = new AuditLog();
            log.setService(getCurrentServiceName());
            log.setAction(action);
            log.setResource(resource);
            log.setUserId(UserContextHolder.getUserId());
            log.setIp(RequestUtil.getClientIp());
            log.setParams(desens.desensitize(
                params != null ? mapper.writeValueAsString(params) : null
            ));
            log.setStatus(error == null ? "SUCCESS" : "FAILURE");
            log.setErrorMessage(error != null ? error.getMessage() : null);
            log.setTimestamp(Instant.now());

            repo.save(log);
        } catch (Exception e) {
            // 审计失败不影响业务
            log.error("审计失败", e);
        }
    }

    public void log(String action, String resource, Object params) {
        log(action, resource, params, null);
    }
}

2.3 异步写入

yaml
spring:
  task:
    execution:
      pool:
        audit:
          core-size: 4
          max-size: 8
          queue-capacity: 1000
java
@Async("auditExecutor")
public void logAsync(...) { ... }

或批量刷盘:

java
@Component
public class AuditBuffer {

    private final BlockingQueue<AuditLog> queue = new LinkedBlockingQueue<>(10000);

    @PostConstruct
    public void flushThread() {
        new Thread(this::drain).start();
    }

    private void drain() {
        List<AuditLog> batch = new ArrayList<>(1000);
        while (true) {
            try {
                AuditLog log = queue.take();
                batch.add(log);
                queue.drainTo(batch, 999);
                if (!batch.isEmpty()) {
                    repo.saveAll(batch);
                    batch.clear();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

2.4 审计 Trace 串联

java
@Component
public class AuditLogAspect {

    @Autowired
    private AuditLogger logger;

    @Around("@annotation(auditAnnotation)")
    public Object audit(ProceedingJoinPoint pjp, Audit auditAnnotation) throws Throwable {
        String action = auditAnnotation.action();
        String resource = pjp.getSignature().getDeclaringTypeName();

        try {
            Object result = pjp.proceed();
            logger.log(action, resource, pjp.getArgs());
            return result;
        } catch (Throwable t) {
            logger.log(action, resource, pjp.getArgs(), t);
            throw t;
        }
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audit {
    String action();
}

@Audit(action = "ORDER_CREATE")
public Order createOrder(OrderRequest req) { ... }

2.5 审计查询界面

sql
-- 7 日内某用户的操作
SELECT * FROM audit_log
WHERE user_id = ? AND timestamp > NOW() - INTERVAL 7 DAY
ORDER BY timestamp DESC;

-- 高频异常用户
SELECT user_id, COUNT(*) cnt
FROM audit_log
WHERE status = 'FAILURE' AND timestamp > NOW() - INTERVAL 1 HOUR
GROUP BY user_id HAVING cnt > 20;

三、GDPR / 个人信息保护法

3.1 合规要求

yaml
告知: 明确告知用途
同意: 主动勾选
最小: 数据不得过度收集
使用目的限制: 只能用收集用途
可见: 用户可查询自己数据
可携: 用户可导出
删除: 用户可被遗忘
安全: 防泄露
响应: 72 小时内报告违规

3.2 同意管理

sql
CREATE TABLE consent (
    user_id BIGINT,
    purpose VARCHAR(50),     -- analytics / marketing / essential
    granted BOOLEAN,
    granted_at TIMESTAMP,
    ip_address VARCHAR(45)
);
java
@Service
public class ConsentService {

    public void grantConsent(Long userId, Purpose purpose, String ip) {
        // 记录同意 + IP + 时间,作为证据
        consentRepo.save(new Consent(userId, purpose, true, Instant.now(), ip));
    }

    public boolean isAllowed(Long userId, Purpose purpose) {
        return consentRepo.findById(new ConsentKey(userId, purpose))
            .map(Consent::isGranted)
            .orElse(false);
    }
}

3.3 数据最小化

java
@Entity
@Table(name = "user")  // 敏感字段加密存储
public class User {

    @Column(nullable = false)
    private String username;

    @Convert(converter = EncryptedStringConverter.class)
    private String phone;       // 加密

    @Convert(converter = EncryptedStringConverter.class)
    private String idCard;     // 加密

    private String city;        // 不存详细地址
}

3.4 用户数据访问

java
@RestController
@RequestMapping("/api/privacy")
public class PrivacyController {

    @GetMapping("/my-data")
    public UserDataExport exportMyData(Long userId) {
        UserDataExport export = new UserDataExport();
        export.setUser(userRepo.findById(userId));
        export.setOrders(orderRepo.findByUser(userId));
        export.setLoginHistory(auditRepo.findLoginsByUser(userId));

        // 生成下载链接,72 小时过期
        String url = fileService.upload(JSON.toJSONString(export));
        return new UserDataExport(url);
    }

    @DeleteMapping("/my-account")
    public void deleteMyAccount(Long userId) {
        // 1. 软删除
        userService.softDelete(userId);
        // 2. 触发链式删除
        kafkaTemplate.send("user-events", new UserDeletedEvent(userId));
    }
}

四、等保 2.0 合规

4.1 五级要求

等级适用
第一级(自主)一般系统
第二级(指导)一般业务
第三级(监督)重要业务(金融、政务、电信)
第四级(强制)极重要
第五级(专控)涉密

4.2 三级系统要求

yaml
物理:
  - 机房电磁屏蔽
  - 视频监控
  - 防雷击

网络:
  - 划分安全区域
  - 网络隔离
  - NTP 时间同步

主机:
  - 操作系统加固
  - 漏洞补丁
  - 入侵检测

应用:
  - 用户认证
  - 访问控制
  - 数据加密
  - 安全审计

数据:
  - 数据加密
  - 备份恢复
  - 完整性校验

4.3 技术要求

4.4 等保整改项

维度整改
访问控制RBAC + 强密码 + MFA
审计集中日志,180 天保留
入侵防范WAF + 主机 HIDS
恶意代码防范防病毒 + 镜像扫描
数据保密加密存储 + 加密传输
数据备份3-2-1 备份
漏洞修复7 天内紧急漏洞

五、PCI-DSS

金融支付卡合规。

yaml
12 项要求:
  1. 安装维护防火墙
  2. 不使用厂商默认配置
  3. 持卡人数据保护
  4. 加密传输
  5. 防病毒软件
  6. 维护安全系统
  7. 按需访问权限
  8. 唯一身份标识
  9. 物理安全
  10. 监控 + 测试
  11. 定期评估
  12. 信息安全政策

关键:

  • CDE(持卡人数据环境)隔离
  • 不存 CVV / PIN
  • 加密存储 PAN

六、应急响应

6.1 事件分类

级别示例
P0大规模数据泄露
P1系统被入侵
P2漏洞被发现
P3异常行为告警

6.2 响应流程

6.3 取证要点

yaml
时间线: 用 NTP 同步的时钟
原始证据: dd 命令镜像磁盘
日志保留: Web/DB/应用/中间件
文件 hash: 留 integrity 校验
内存 dump: vmstat / memdump / Volatility
网络: tcpdump,IP 记录

6.4 通知合规要求

yaml
GDPR:
  - 72 小时内通知监管
  - 用户高风险时也要通知

中国数据安全法:
  - 立即向监管部门报告
  - 重大事件向社会发布

七、合规检查清单

7.1 等保 三级 自查

yaml
✓ 机房 / 物理安全
✓ 网络架构合理划分
✓ 网络设备配置基线
✓ 操作系统加固
✓ 数据库配置基线
✓ 应用最小权限
✓ 身份认证(MFA)
✓ 访问控制(RBAC)
✓ 审计日志 ≥ 180 天
✓ 数据加密(传输 + 存储)
✓ 入侵检测/防御
✓ 漏洞修复 ≤ 7 天
✓ 备份演练
✓ 应急预案
✓ 安全管理机构
✓ 人员安全

7.2 应用层安全自查

yaml
检查点:
  - SQL 注入:参数化查询
  - XSS:输出转义
  - CSRF:Token 校验
  - SSRF:URL 白名单
  - 越权:RBAC + 资源校验
  - 弱密码:策略强制
  - 密钥管理:Vault
  - 日志:不记敏感信息
  - 异常处理:不泄露堆栈
  - 依赖漏洞:SCA 扫描
  - HTTPS:全站启用
  - HSTS:启用

7.3 工具化检查

bash
# 依赖漏洞
trivy fs ./
snyk test

# 镜像扫描
trivy image myapp:1.0

# 端口扫描
nmap -sV myhost.example.com

# 配置检查
nuclei -u https://myapp.example.com

八、第三方与供应商

8.1 风险评估

yaml
评估维度:
  - 数据访问范围
  - 技术能力
  - 安全资质(ISO 27001 / SOC 2)
  - 历史安全事件
  - 合同条款

8.2 数据处理协议(DPA)

yaml
必备条款:
  - 数据处理目的与方式
  - 安全义务
  - 保密义务
  - 数据主体权利响应
  - 子处理者管理
  - 数据销毁
  - 审计权
  - 违约责任
  - 跨境传输

8.3 风险监控

yaml
定期评估: 至少每年一次
现场审计: 关键供应商
合规报告: SOC 2 / ISO
事件通知: 24 小时内通报

九、用户隐私体验

9.1 隐私政策

yaml
必备内容:
  - 数据收集清单
  - 数据使用目的
  - 数据共享第三方
  - 数据存储期限
  - 用户权利
  - 联系方式
  - 更新通知
html
<div id="cookie-banner">
    我们使用 Cookie 提供服务,继续浏览视为同意。
    <a href="/privacy">隐私政策</a>
    <button id="accept">同意</button>
    <button id="reject">拒绝</button>
</div>

9.3 隐私设计(PbD)

yaml
七原则:
  1. 预防(Proactive not Remediation)
  2. 默认隐私(Privacy as Default)
  3. 嵌入设计(Privacy Embedded)
  4. 全功能(Positive-Sum, not Zero-Sum)
  5. 端到端安全(End-to-End Security)
  6. 可见透明(Visibility and Transparency)
  7. 用户尊重(Respect for User Privacy)

十、本章小结

主题关键
审计集中日志 + 不可篡改 + 长期保留
GDPR数据最小 + 同意 + 被遗忘
等保 2.0技术 + 管理 双体系
PCI-DSSCDE 隔离 + 不存 PAN
应急取证 + 通知 + 复盘

动手练习

  1. 实现审计日志系统,记录所有敏感操作
  2. 给你的 API 加 RBAC + 资源访问检查
  3. 写数据导出接口,用户可下载自己的数据
  4. 配置审计日志保留 180 天,异地归档

下一章:第 239 章:性能调优 - JVM

本站基于 VitePress 构建 · 由 Codebook 团队维护