AI 辅助代码安全审计——OWASP Top 10 扫一遍
AI 辅助代码安全审计——OWASP Top 10 扫一遍
适读人群:需要做代码安全审查的后端开发者 | 阅读时长:约 13 分钟 | 核心价值:了解 AI 安全审计的真实能力边界,以及如何把它整合进安全审查流程
半年前,我把自己写的一个 API 服务代码拿给 AI 做了一次安全审计。
那个服务是我做 AI 转型之后接的第一个外包项目,一个企业内部的文档管理系统,有用户认证、文件上传、权限控制。代码大概 8000 行 Java,我觉得写得还算规范。
AI 一共给我报出了 11 个问题。
其中 4 个是真实的安全漏洞,其中 1 个是比较严重的。
剩下 7 个有 3 个是误报,4 个是"理论上存在风险但在这个系统的上下文里不是实际问题"。
对于一个自认为写得不错的代码来说,发现 4 个真实问题,这个结果挺让我不安的。这篇文章就写这次审计的过程和结论。
我的审计方式
我没有直接把 8000 行代码全丢给 AI,那样太低效。我按照 OWASP Top 10 的分类,逐个模块地进行审计。
每次审计一个模块,附上模块的上下文(用了哪些框架、数据库操作方式、认证方式),然后让 AI 针对特定的漏洞类型进行分析。
Prompt 模板大概是这样的:
你是一位安全工程师,请对以下 Java 代码进行安全审计。
重点关注 OWASP Top 10 中的 [具体漏洞类型]。
上下文信息:
- 框架:Spring Boot 3.x + Spring Security
- 数据库:MySQL,使用 MyBatis
- 认证方式:JWT Token
- 该模块功能:[功能描述]
代码如下:
[代码]
请列出:
1. 确认的安全漏洞(附上具体代码位置和利用方式)
2. 潜在风险(需要进一步确认的)
3. 安全建议
如果代码没有问题,直接说没有发现漏洞,不要为了显得有用而找不存在的问题。真实发现的漏洞
漏洞 1(严重):SQL 注入
AI 在审计文件搜索功能时发现了这个:
@Mapper
public interface DocumentMapper {
// 这是有问题的写法
@Select("SELECT * FROM documents WHERE title LIKE '%" + "#{keyword}" + "%'")
List<Document> searchByTitle(@Param("keyword") String keyword);
}等等,这看起来用了 #{},应该是安全的?
AI 的分析让我出了一身冷汗:这是 MyBatis 的一个常见误解。#{} 会做参数化处理,但这里的写法是把占位符嵌在字符串里,而不是作为独立参数。实际生成的 SQL 取决于 MyBatis 的处理方式,在某些版本和配置下,这段代码实际上等同于字符串拼接。
安全的写法应该是:
@Select("SELECT * FROM documents WHERE title LIKE CONCAT('%', #{keyword}, '%')")
List<Document> searchByTitle(@Param("keyword") String keyword);或者使用动态 SQL:
<select id="searchByTitle" resultType="Document">
SELECT * FROM documents
WHERE title LIKE CONCAT('%', #{keyword}, '%')
</select>我后来验证了一下,在我用的 MyBatis 版本里,原来的写法确实存在注入风险。这是一个真实的高危漏洞,被 AI 抓出来了。
漏洞 2(中危):不安全的文件上传
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(
@RequestParam("file") MultipartFile file,
HttpServletRequest request) {
String filename = file.getOriginalFilename(); // 直接使用原始文件名
String uploadPath = "/data/documents/" + filename;
file.transferTo(new File(uploadPath));
return ResponseEntity.ok("上传成功:" + filename);
}AI 指出了三个问题:
- 使用用户提供的原始文件名,存在路径穿越攻击风险(如
../../etc/passwd) - 没有文件类型验证,可以上传可执行文件
- 文件名没有做唯一化处理,同名文件会覆盖
修复方案:
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(
@RequestParam("file") MultipartFile file,
@AuthenticationPrincipal UserDetails user) {
// 1. 验证文件类型(只允许特定类型)
String contentType = file.getContentType();
Set<String> allowedTypes = Set.of(
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
if (!allowedTypes.contains(contentType)) {
return ResponseEntity.badRequest().body("不支持的文件类型");
}
// 2. 生成安全的文件名(不使用原始文件名)
String extension = getExtension(file.getOriginalFilename());
String safeFilename = UUID.randomUUID().toString() + "." + extension;
// 3. 限制上传路径(不允许路径穿越)
Path uploadDir = Paths.get("/data/documents").toAbsolutePath().normalize();
Path targetPath = uploadDir.resolve(safeFilename).normalize();
if (!targetPath.startsWith(uploadDir)) {
throw new SecurityException("非法的文件路径");
}
file.transferTo(targetPath.toFile());
return ResponseEntity.ok("上传成功");
}漏洞 3(中危):JWT 没有过期时间验证
public boolean validateToken(String token) {
try {
Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token);
return true;
} catch (JwtException e) {
return false;
}
}这段代码只验证了签名,没有显式验证过期时间。虽然 JJWT 库在解析时默认会验证过期时间,但 AI 指出:如果 secretKey 泄露或者以后换了解析逻辑,这种隐式依赖会成为安全隐患。更重要的是,代码没有实现 token 吊销机制,一旦 token 泄露,在过期前无法撤销。
漏洞 4(低危):日志泄露敏感信息
log.info("用户登录请求: userId={}, password={}", request.getUserId(), request.getPassword());明文密码写进了日志。这个问题我自己觉得挺低级的,确实是犯了个明显的错误。
AI 的误报
误报 1:错误地认为存在 SSRF
AI 看到这段代码:
public String fetchExternalContent(String url) {
return restTemplate.getForObject(url, String.class);
}就说存在 SSRF(服务端请求伪造)漏洞。
但 AI 没看到这个方法只被一个内部的管理员接口调用,而且调用方有严格的管理员权限校验。普通用户根本访问不到这个接口。
所以这不是一个实际可利用的漏洞,是误报。当然,AI 的建议(加 URL 白名单)是合理的防御性建议,但它把这定性为"确认漏洞"是不准确的。
误报 2:过度解读序列化
AI 看到项目里用了 Java 序列化(ObjectOutputStream),就报了"不安全的反序列化漏洞"。
但这段序列化代码用于本地缓存文件,不接受任何外部输入。反序列化漏洞的前提是接受不可信来源的序列化数据,这里不存在这个条件。
误报 3:对 Spring Security 默认配置的误解
AI 说"没有配置 CSRF 保护"是一个漏洞,但我们的 API 是纯 REST API,使用 JWT 认证,没有 session,CSRF 根本不适用。这是 AI 对上下文理解不足导致的。
AI 安全审计的能力边界
基于这次审计和后续的几次实践,我对 AI 安全审计的能力有了比较清晰的判断:
AI 擅长的漏洞类型:
- SQL 注入(对代码模式比较敏感)
- 不安全的文件操作(路径穿越、文件类型验证缺失)
- 明显的硬编码凭证(密码、API Key 直接写在代码里)
- 日志打印敏感信息
- 常见的加密误用(MD5 存密码、弱随机数)
AI 不擅长的漏洞类型:
- 业务逻辑漏洞(水平越权、垂直越权——需要理解整个业务上下文)
- 复杂的认证授权绕过(需要理解完整的调用链)
- 竞态条件(Time-of-check-to-time-of-use)
- 依赖库的已知漏洞(需要结合 CVE 数据库)
AI 容易误报的情况:
- 看到某种模式就报漏洞,不考虑实际可利用性
- 对安全框架(Spring Security 等)的默认行为理解不足
- 把防御性建议当成"确认漏洞"输出
合理的使用方式
我现在的安全审计流程是:
自动化工具先跑一遍:SonarQube 扫代码质量,OWASP Dependency-Check 扫依赖漏洞。这些工具快且准确,能覆盖大量已知模式。
AI 做补充审计:重点让 AI 看业务逻辑相关的代码,特别是输入处理、文件操作、权限检查。这些地方 AI 比静态分析工具更有优势。
人工验证 AI 的发现:AI 的每一个发现都需要人工确认是否真实可利用。不要直接采纳 AI 的"严重程度"判断。
对 AI 的误报保持预期:误报率大概在 30-40%,要有耐心去逐一排除。
AI 安全审计不能替代专业的安全测试,但作为开发者日常的安全自检工具,它已经可以帮你抓住相当一部分真实问题。那次审计里发现的 SQL 注入漏洞,如果没有 AI,我很可能在代码上线之后才意识到。
