第 236 章:数据安全与合规
学习目标
- 数据加密方案
- 隐私信息保护
- 法律法规合规
- 数据备份与恢复
一、数据安全威胁
二、加密技术
2.1 对称加密 vs 非对称加密
| 维度 | 对称 | 非对称 |
|---|---|---|
| 密钥 | 同一把 | 一对(公/私) |
| 性能 | 快 | 慢 |
| 用途 | 大量数据加密 | 密钥交换、签名 |
| 算法 | AES、DES、SM4 | RSA、ECC、SM2 |
2.2 AES 加密
java
public class AESUtil {
private static final String KEY = "my-256-bit-key-...";
private static final String IV = "1234567890abcdef"; // 16 字节
public static String encrypt(String data) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(IV.getBytes()));
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decrypt(String encrypted) throws Exception {
byte[] data = Base64.getDecoder().decode(encrypted);
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(IV.getBytes()));
byte[] original = cipher.doFinal(data);
return new String(original, StandardCharsets.UTF_8);
}
}2.3 国密 SM4/SM2
xml
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>java
public class SM4Util {
public byte[] encrypt(byte[] data, byte[] key) {
// SM4 算法
}
}2.4 字段级加密
java
@Entity
public class User {
@Convert(converter = EncryptedStringConverter.class)
@Column(name = "phone")
private String phone; // 数据库里是密文
@Convert(converter = EncryptedStringConverter.class)
private String idCard;
}
@Converter
public class EncryptedStringConverter implements AttributeConverter<String, String> {
@Override
public String convertToDatabaseColumn(String attribute) {
return AESUtil.encrypt(attribute);
}
@Override
public String convertToEntityAttribute(String dbData) {
return AESUtil.decrypt(dbData);
}
}2.5 密钥管理
yaml
# ❌ 不应放在代码中
private static final String KEY = "hardcoded-secret";
# ✅ 通过 Vault / KMS 动态获取
@Autowired
private VaultTemplate vaultTemplate;
String key = vaultTemplate.read("secret/data/appkey")
.getData().get("aes_key");工具:
- HashiCorp Vault
- AWS KMS
- 阿里云 KMS
- 腾讯云 KMS
三、敏感数据保护
3.1 数据分级
| 级别 | 数据 |
|---|---|
| L4 绝密 | 身份证、银行密码 |
| L3 机密 | 银行卡号、医疗记录 |
| L2 秘密 | 手机号、邮箱 |
| L1 公开 | 公开资料 |
3.2 手机号脱敏
java
public static String maskPhone(String phone) {
if (phone == null || phone.length() != 11) return phone;
return phone.substring(0, 3) + "****" + phone.substring(7);
// 138****8888
}
public static String maskIdCard(String idCard) {
if (idCard == null || idCard.length() < 8) return idCard;
return idCard.substring(0, 4) + "**********" + idCard.substring(14);
// 1101**********1234
}3.3 邮箱脱敏
java
public static String maskEmail(String email) {
int atIndex = email.indexOf("@");
if (atIndex <= 1) return email;
return email.charAt(0) + "***" + email.substring(atIndex);
// t***@gmail.com
}3.4 JSON 序列化脱敏
java
@JsonSerialize(using = PhoneMaskSerializer.class)
private String phone;
public class PhoneMaskSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeString(MaskUtil.maskPhone(value));
}
}3.5 日志脱敏
java
public class LogMaskUtil {
private static final Pattern PHONE = Pattern.compile("\\b1[3-9]\\d{9}\\b");
private static final Pattern ID_CARD = Pattern.compile("\\b\\d{17}[\\dXx]\\b");
private static final Pattern EMAIL = Pattern.compile("\\b[\\w.]+@[\\w.]+\\b");
public static String mask(String log) {
log = PHONE.matcher(log).replaceAll("$1****$2");
log = ID_CARD.matcher(log).replaceAll("$1***$2");
log = EMAIL.matcher(log).replaceAll("***@***");
return log;
}
}
// 在 logback 中插入脱敏 Appender
public class MaskAppender extends AppenderBase<ILoggingEvent> {
@Override
protected void append(ILoggingEvent event) {
String msg = LogMaskUtil.mask(event.getFormattedMessage());
System.out.println(msg);
}
}四、备份与恢复
4.1 3-2-1 原则
4.2 MySQL 备份
物理备份(XtraBackup)
bash
# 全量备份
xtrabackup --backup --target-dir=/backup/full \
--user=root --password=xxx
# 增量备份
xtrabackup --backup --target-dir=/backup/inc1 \
--incremental-basedir=/backup/full
# 恢复
xtrabackup --prepare --target-dir=/backup/full
xtrabackup --copy-back --target-dir=/backup/full逻辑备份(mysqldump)
bash
mysqldump -uroot -p \
--single-transaction \
--routines --triggers --events \
--databases mydb > mydb_20260813.sql4.3 Redis 持久化
yaml
# AOF 方式,记录所有写命令
appendonly yes
appendfsync everysecbash
# 导出 RDB
redis-cli save
# 或 BGSAVE 异步
redis-cli bgsave4.4 备份验证
bash
# 每月演练:还原到测试环境
mysql -u root < backup_202607.sql
# 验证
SELECT count(*) FROM orders;
# 检查行数是否一致4.5 数据库恢复 RPO / RTO
| 类型 | 含义 | 设计 |
|---|---|---|
| RPO | 可容忍的数据丢失 | 主从复制 < 1 秒 |
| RTO | 恢复服务的时间 | 备库切换 < 1 分钟 |
五、传输安全
5.1 TLS/HTTPS
yaml
server:
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: changeit
key-store-type: PKCS12
key-alias: myapp
protocol: TLS
enabled-protocols: TLSv1.2,TLSv1.35.2 mTLS(双向认证)
yaml
server:
ssl:
enabled: true
key-store: classpath:server.p12
trust-store: classpath:truststore.p12
client-auth: need # 强制客户端证书5.3 数据库连接加密
yaml
spring:
datasource:
url: jdbc:mysql://host:3306/db?useSSL=true&requireSSL=true
hikari:
data-source-properties:
sslMode: REQUIRED5.4 Redis TLS
bash
redis-server --tls-port 6379 \
--tls-cert-file /path/server.crt \
--tls-key-file /path/server.key \
--tls-ca-cert-file /path/ca.crt六、数据库安全
6.1 最小权限
sql
-- ❌ 应用程序用 root
GRANT ALL ON *.* TO 'app'@'%' IDENTIFIED BY 'pass';
-- ✅ 只给必要权限
GRANT SELECT, INSERT, UPDATE ON orders.* TO 'order_app'@'%';
GRANT SELECT ON users.user TO 'order_app'@'%'; -- 读用户表
REVOKE DELETE ON orders.* FROM 'order_app'@'%';6.2 网络隔离
yaml
# 数据库只允许应用网段访问
bind-address = 10.0.0.10 # 内网
# iptables
iptables -A INPUT -p tcp --dport 3306 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP6.3 SQL 审计
yaml
# MySQL 8 开启审计
plugin-load = audit_log.so
audit_log_format = JSON
audit_log_policy = ALL6.4 凭据轮换
java
@Component
public class CredentialRotation {
@Scheduled(cron = "0 0 0 1 * ?")
public void rotateDbPassword() {
String newPass = generatePassword();
jdbcTemplate.update("SET PASSWORD = PASSWORD(?)", newPass);
vaultTemplate.write("secret/data/mysql", Map.of("password", newPass));
}
}七、隐私合规
7.1 GDPR / 中国《个人信息保护法》
yaml
合规要求:
告知: 用明示告知用户收集目的
同意: 用户主动勾选同意
访问: 用户有查看自己数据的权利
删除: 用户可要求删除数据(被遗忘权)
修正: 用户可更新错误信息
限制: 数据只能用于收集目的7.2 用户"被遗忘权"实现
java
@Service
public class UserDeletionService {
@Transactional
public void deleteUser(Long userId) {
// 1. 软删除用户
userRepo.setDeletedAt(userId, Instant.now());
// 2. 删除关联数据(订单、地址等)
orderRepo.anonymizeByUser(userId); // 匿名化
addressRepo.deleteByUser(userId); // 删除
// 3. 删除本地缓存
cacheService.delete("user:" + userId);
// 4. 通知其他服务清理
kafkaTemplate.send("user-events", new UserDeletedEvent(userId));
}
}7.3 匿名化 vs 假名化
匿名化: 完全不可逆(删除、聚合)
假名化: 用代号替代(数据仍可关联,但不直接对应个人)java
public class Anonymizer {
public Order anonymize(Order order) {
order.setUserId(0L); // 假名化,用 0 代表被删除用户
order.setRecipientName("已删除用户");
order.setPhone("00000000000");
return order;
}
}八、应用层安全配置
8.1 关闭危险端点
yaml
management:
endpoints:
web:
exposure:
include: "health,info,prometheus" # 不暴露 env、heapdump8.2 屏蔽错误堆栈
yaml
server:
error:
include-stacktrace: never # 生产环境不暴露堆栈
include-message: never
whitelabel:
enabled: false8.3 禁用非必要 HTTP 方法
java
@Configuration
public class HttpSecurityConfig {
@Bean
public SecurityFilterChain filter(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.headers(headers -> headers
.frameOptions(FrameOptionsConfig::deny) // 防 clickjacking
.xssProtection(Customizer.withDefaults()) // 防 XSS
.contentTypeOptions(Customizer.withDefaults()) // 防 MIME sniffing
)
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
}
}九、密钥安全
9.1 加密机
yaml
国密合规:
- 服务器密码机: 加密密钥专用硬件
- UKey / 智能卡: 双因素认证
- SM2/SM3/SM4 算法9.2 HSM(硬件安全模块)
| 厂商 | 产品 |
|---|---|
| AWS | CloudHSM |
| Azure | Dedicated HSM |
| 阿里云 | KMS |
| 自建 | Thales Luna |
9.3 密钥生命周期
9.4 代码中密钥
java
// ❌ 不要 hardcode
private static final String SECRET = "abcd";
// ✅ 运行时获取
@Value("${myapp.secret}")
private String secret;
// 通过 Vault / 启动参数注入十、第三方依赖安全
10.1 OWASP Dependency-Check
xml
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
</plugin>bash
mvn dependency-check:check
# 生成: target/dependency-check-report.html10.2 SCA 工具
- Trivy:扫描镜像 + 文件系统
- Snyk:开源 + 商业
- Mend(原 WhiteSource)
- 腾讯云 TSCS
10.3 依赖版本控制
xml
<!-- 统一管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.15.3</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>定期更新,跟进 CVE 公告。
十一、本章小结
| 主题 | 关键措施 |
|---|---|
| 数据加密 | AES、字段级加密 |
| 密钥管理 | Vault、KMS |
| 脱敏 | 手机、身份证、邮箱 |
| 传输 | TLS / mTLS |
| 备份 | 3-2-1 原则 |
| 合规 | GDPR、个人信息保护法 |
动手练习
- 用 AES 实现字段级加密(用户身份证)
- 用 Vault 管理密钥,动态获取
- 写一个手机号脱敏函数,加进日志输出
- 配置 mTLS,实现微服务间双向认证
下一章:第 237 章:系统与应用安全