前言

AI 编程助手确实能提高效率,但安全问题不容忽视。去年我们团队在使用 AI 生成代码时,连续踩了几个安全坑,差点导致生产环境事故。这篇文章记录这些教训和防范措施。

坑一:AI 生成的代码存在 SQL 注入漏洞

问题代码

让 AI 生成一个用户登录接口:

1
2
3
4
5
6
7
8
9
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
// AI 生成的代码(有漏洞!)
String sql = "SELECT * FROM users WHERE username = '" + request.getUsername()
+ "' AND password = '" + request.getPassword() + "'";

User user = jdbcTemplate.queryForObject(sql, User.class);
// ...
}

AI 居然生成了字符串拼接 SQL!这在 2024 年简直是不可想象的。

攻击演示

1
2
3
curl -X POST http://api.example.com/login \
-H "Content-Type: application/json" \
-d '{"username":"admin'\'' OR '\''1'\''='\''1","password":"anything"}'

直接绕过密码验证,以 admin 身份登录。

正确写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";

try {
User user = jdbcTemplate.queryForObject(sql,
new BeanPropertyRowMapper<>(User.class),
request.getUsername(),
hashPassword(request.getPassword())
);
// ...
} catch (EmptyResultDataAccessException e) {
return ResponseEntity.status(401).body("认证失败");
}
}

教训

AI 生成的代码可能包含过时的安全实践。它的训练数据包含大量历史代码,包括那些写于安全规范普及之前的代码。

坑二:AI 建议引入有漏洞的依赖

问题场景

需要处理 Excel 文件,询问 AI:

1
2
Q: Java 如何读取 Excel 文件?
A: 可以使用 Apache POI,在 pom.xml 中添加:
1
2
3
4
5
6
<!-- AI 建议的版本(有漏洞) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>

漏洞核查

用 OWASP Dependency-Check 扫描:

1
mvn org.owasp:dependency-check-maven:check

结果:

1
2
3
4
poi-ooxml-3.15.jar
CVE-2017-5644: 7.5 (High)
CVE-2019-12415: 7.5 (High)
CVE-2022-26336: 7.5 (High)

3.15 版本有 3 个高危漏洞!最新安全版本是 5.2.3+。

解决方案

1. 引入依赖前强制检查

1
2
3
# 添加到 CI/CD 流水线
mvn org.owasp:dependency-check-maven:check \
-DfailBuildOnCVSS=7

2. 使用版本管理工具

1
2
3
4
5
6
7
8
9
10
<properties>
<!-- 统一在 properties 中管理版本 -->
<poi.version>5.2.3</poi.version>
</properties>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>

3. 定期扫描

1
2
# 每周执行一次
0 2 * * 1 mvn org.owasp:dependency-check-maven:check

坑三:AI 生成的正则表达式存在 ReDoS 漏洞

问题代码

验证邮箱格式的正则:

1
2
3
4
5
// AI 生成的正则(存在 ReDoS)
public boolean isValidEmail(String email) {
String regex = "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$";
return email.matches(regex);
}

看起来没问题?但测试一下:

1
2
3
4
5
String malicious = "a".repeat(100000) + "@test.com";
long start = System.currentTimeMillis();
isValidEmail(malicious); // 卡死!CPU 100%
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - start) + "ms"); // 永远跑不完

原因分析

正则中的 ([a-zA-Z0-9_\-\.]+) 使用了贪婪匹配,特定输入会导致回溯爆炸(Catastrophic Backtracking)。

安全写法

1
2
3
4
5
6
7
8
9
10
public boolean isValidEmail(String email) {
// 限制输入长度
if (email == null || email.length() > 254) {
return false;
}

// 使用更安全的正则,避免嵌套量词
String regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
return email.matches(regex);
}

或者使用专门的验证库:

1
2
3
// Apache Commons Validator
EmailValidator validator = EmailValidator.getInstance();
return validator.isValid(email);

坑四:AI 生成的代码泄露敏感信息到日志

问题代码

1
2
3
4
5
6
7
8
9
10
@Slf4j
@Service
public class PaymentService {

public void processPayment(PaymentRequest request) {
// AI 生成的日志(泄露敏感信息!)
log.info("处理支付请求: {}", request);
// 输出: 处理支付请求: PaymentRequest(cardNo=622202******, cvv=123, ...)
}
}

AI 直接建议打印整个对象,导致信用卡号、CVV 等敏感信息进入日志系统。

正确做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Slf4j
@Service
public class PaymentService {

public void processPayment(PaymentRequest request) {
// 只记录非敏感信息
log.info("处理支付请求, 订单号: {}, 金额: {}",
request.getOrderNo(),
request.getAmount()
);

// 敏感信息单独处理,不记录
String cardNo = request.getCardNo();
String maskedCardNo = maskCardNumber(cardNo);
log.debug("银行卡号(脱敏): {}", maskedCardNo);
// 输出: 银行卡号(脱敏): 622202******1234
}

private String maskCardNumber(String cardNo) {
if (cardNo == null || cardNo.length() < 8) {
return "****";
}
return cardNo.substring(0, 4) + "******"
+ cardNo.substring(cardNo.length() - 4);
}
}

我们的 AI 代码安全审查流程

阶段一:生成时预防

1. 安全提示词模板

1
2
3
4
5
6
生成代码时请遵循以下安全规范:
1. 所有数据库操作使用参数化查询
2. 用户输入必须验证和转义
3. 敏感信息不得硬编码
4. 使用最新的依赖版本
5. 避免使用有已知漏洞的算法

2. 敏感代码标记

1
2
3
4
5
// SECURITY_REVIEW_REQUIRED: AI Generated
// 这段代码由 AI 生成,需要人工安全审查
public void processUserInput(String input) {
// ...
}

阶段二:提交前检查

自动化安全检查清单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
# ai-security-check.sh

echo "AI 生成代码安全检查"

# 1. 检查 SQL 注入风险
echo "检查 SQL 注入..."
grep -rn "executeQuery.*+" src/ || true
grep -rn "Statement.*execute" src/ || true

# 2. 检查硬编码密钥
echo "检查硬编码密钥..."
grep -rn "password.*=.*[\"']" src/ || true
grep -rn "secret.*=.*[\"']" src/ || true
grep -rn "api.*key.*=.*[\"']" src/ || true

# 3. 检查不安全的反序列化
echo "检查反序列化..."
grep -rn "ObjectInputStream" src/ || true

# 4. 依赖漏洞扫描
echo "依赖漏洞扫描..."
mvn org.owasp:dependency-check-maven:check -q

echo "检查完成!"

阶段三:代码审查强化

AI 生成代码审查检查表:

检查项 风险等级 检查方法
SQL 注入 确认使用参数化查询
XSS 确认输出转义
敏感信息泄露 检查日志和异常信息
依赖漏洞 OWASP 扫描
正则 ReDoS 测试超长输入
硬编码密钥 全局搜索
不安全的随机数 使用 SecureRandom
路径遍历 验证文件路径

阶段四:运行时监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class SecurityMonitor {

@EventListener
public void onAuthenticationFailure(AuthenticationFailureEvent event) {
// 监控异常登录,可能是 AI 代码逻辑漏洞导致
log.warn("认证失败: {}, 次数: {}",
event.getAuthentication().getName(),
getFailureCount(event.getAuthentication().getName())
);
}

@Scheduled(fixedRate = 60000)
public void checkSuspiciousActivity() {
// 检查是否有异常的 SQL 执行模式
// 检查是否有大量敏感信息访问
}
}

AI 代码安全工具推荐

工具 用途 集成方式
SonarQube 静态代码分析 CI/CD 流水线
OWASP Dependency-Check 依赖漏洞扫描 Maven/Gradle 插件
CodeQL 语义代码分析 GitHub Actions
Semgrep 轻量级静态分析 CLI 或 CI
Snyk 依赖和应用安全 IDE 插件

总结

AI 生成代码的安全问题不是 AI 本身的问题,而是:

  1. 训练数据的局限性:包含大量历史代码,包括不安全的写法
  2. 上下文理解不足:无法知道你的安全需求和合规要求
  3. 缺乏领域知识:不了解你的业务场景中的敏感信息

安全使用 AI 的原则:

  • 把 AI 当作”初级程序员”,代码必须审查
  • 涉及安全的代码(认证、支付、加密),AI 只给参考,不直接采用
  • 建立 AI 代码的安全审查流程
  • 定期扫描依赖漏洞
  • 运行时监控异常行为

AI 是工具,安全是责任。不能因为有了 AI,就放松对代码质量的要求。

参考