首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >每天1个skill系列5:Security Auditor你在上线前抓出来AI写的登录代码藏的几个漏洞

每天1个skill系列5:Security Auditor你在上线前抓出来AI写的登录代码藏的几个漏洞

原创
作者头像
李福春
发布2026-06-30 09:16:41
发布2026-06-30 09:16:41
4271
举报

"知彼知己,百战不殆;不知彼而知己,一胜一负;不知彼,不知己,每战必殆。" ——孙子《谋攻篇》


老李盯着屏幕,有点后怕。

不是代码跑崩了,是安全团队发来的一份扫描报告。上面写着:生产环境的登录接口,存在一个SQL注入漏洞,已经上线三个月了。

漏洞很简单,就是用户名字段没有用参数化查询,直接拼接进了SQL。不是什么高明的攻击手法,是个初级问题。

小王去翻了当时的 commit 记录。那段代码,是 Claude Code 生成的,他当时看了一眼能跑,就提上去了。

老李问了一句:"你让它做安全检查了吗?"

小王沉默了几秒。

这就是2026年 AI 辅助编程的另一个真实困境——AI 生成功能的速度很快,但它不会主动告诉你它埋了什么坑。

老李后来找到了 Security Auditor 这个 skill,装上去,回头跑了一遍。

结果出来,他叹了口气。

不止一个。


它是什么?一个在上线前替你过安全关的 Agent

Security Auditor 是一个 Claude Code skill,用于对代码做结构化安全审计,并输出带优先级排序和修复命令的报告。来自 wrsmith108/claude-skill-security-auditor,同时在 alirezarezvani/claude-skills 这套工程技能库里也有一个更完整的版本,覆盖了 OWASP Top 10 全部分类。

它做的事情:

  • 扫描 OWASP Top 10 漏洞类型
  • 检查依赖包的已知 CVE(对 Java 走 mvn dependency-check:check
  • 检测硬编码的 API Key、密码、Token
  • 审查认证和授权逻辑的缺陷
  • 输出按严重程度分级的报告(CRITICAL / HIGH / MEDIUM / LOW)
  • 支持 security-exceptions.json 白名单(已知风险,有审批记录)
  • CI-friendly 的退出码,--fail-on high 可以直接卡 Pipeline

它不做的:修复漏洞(它告诉你在哪,怎么改,动手的是你)。


为什么 AI 生成的代码容易带安全漏洞?

"凡战者,以正合,以奇胜。" ——孙子《兵势篇》

这个问题我觉得值得说清楚,因为很多团队装了 AI 写代码工具之后,安全意识反而松了。

AI 写功能代码很擅长,它知道怎么实现登录、注册、JWT、分页。但它在安全这件事上有几个天然的弱点:

1. 倾向于展示功能,而不是防御性编程

AI 生成代码的目标是"让需求跑通"。一个快速实现的登录功能,它优先考虑的是流程通,不是攻击面。所以字符串拼接 SQL 会出现,因为它功能上没问题。

2. 不同上下文之间的安全知识不连贯

你在这个会话里告诉它用参数化查询,下一个会话它不记得。这是 AI 工具的架构决定的,不是 bug。每次生成都是独立的,安全规范需要反复注入。

3. 异常处理的"兜底式"写法暴露信息

代码语言:java
复制
// AI 常见写法
catch (Exception e) {
    return ResponseEntity.status(500)
        .body("系统异常: " + e.getMessage());  // 把异常信息暴露给客户端了
}

e.getMessage() 直接返回给客户端,是信息泄露。攻击者可以从这里获取你的内部实现信息。

4. 默认配置往往不安全

JWT 的 secret 直接写在代码里,密码加密用 MD5,Spring Boot Actuator 接口没有鉴权……这些都是 AI 生成"能跑"代码时常见的默认配置。

Security Auditor 的价值就在这里。它是一个从安全视角重新过一遍的质检程序,在问题到生产之前把它挡下来。


架构图:在Java+SpringBoot登录系统里的完整位置

"不战而屈人之兵,善之善者也。" ——孙子《谋攻篇》

Security Auditor 在 Code Simplifier 之后运行。逻辑是:先让代码可读,再做安全检查。可读的代码安全问题也更容易发现。


实战:老李的登录模块安全扫描

"故善战者,求之于势,不责于人。" ——孙子《兵势篇》

还是小王那套登录+注册+个人中心。跑 Security Auditor,指令:

security audit on the auth module

扫描报告输出(精简版):

╔══════════════════════════════════════════════╗

║ SECURITY AUDIT REPORT ║

║ Module: auth (Login/Register/Profile) ║

║ Verdict: ❌ FAIL ║

╠══════════════════════════════════════════════╣

║ 🔴 CRITICAL: 2 🟡 HIGH: 3 🟠 MEDIUM: 4 ║

╚══════════════════════════════════════════════╝

一条一条看。


漏洞1:SQL注入(CRITICAL)

代码语言:java
复制
// AuthRepository.java - BEFORE,原始 AI 生成
@Query(value = "SELECT * FROM users WHERE username = '" + 
    "?1' AND status = 'active'", nativeQuery = true)
User findActiveUserByUsername(String username);

这是字符串拼接写法。输入 admin' OR '1'='1 就能绕过认证。

Security Auditor 的报告:

🔴 CRITICAL A03-INJECTION AuthRepository.java:15

Pattern: String concatenation in native SQL query

Risk: SQL injection via username parameter — authentication bypass possible

CVE Reference: CWE-89

Fix: Use parameterized queries or Spring Data JPA method naming

修复后:

代码语言:java
复制
// 方案一:Spring Data JPA 方法命名(最干净)
Optional<User> findByUsernameAndStatus(String username, UserStatus status);

// 方案二:@Query 参数绑定(需要 native query 的场景)
@Query(value = "SELECT * FROM users WHERE username = :username AND status = 'active'", 
       nativeQuery = true)
Optional<User> findActiveUserByUsername(@Param("username") String username);

漏洞2:硬编码 JWT Secret(CRITICAL)

代码语言:java
复制
// JwtUtil.java - BEFORE
private static final String SECRET_KEY = "mySecretKey123456";

public String generateToken(String username, String role) {
    return Jwts.builder()
        .setSubject(username)
        .claim("role", role)
        .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
        .compact();
}

Secret 写死在代码里,进了 git 仓库,就是暴露了。

Security Auditor 报告:

🔴 CRITICAL A02-CRYPTOGRAPHIC JwtUtil.java:8

Pattern: Hardcoded cryptographic secret

Risk: JWT tokens can be forged if secret is exposed via git history

Fix: Load from environment variable or secrets manager

修复后:

代码语言:java
复制
// JwtUtil.java - AFTER
@Value("${jwt.secret}")
private String secretKey;

@Value("${jwt.expiration:86400000}")
private long expirationMs;

public String generateToken(String username, String role) {
    return Jwts.builder()
        .setSubject(username)
        .claim("role", role)
        .setExpiration(new Date(System.currentTimeMillis() + expirationMs))
        .signWith(SignatureAlgorithm.HS256, secretKey)
        .compact();
}

application.yml 里引用环境变量:

代码语言:yaml
复制
jwt:
  secret: ${JWT_SECRET}
  expiration: 86400000

漏洞3:异常信息泄露(HIGH)

代码语言:java
复制
// AuthController.java - BEFORE
} catch (Exception e) {
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
        .body(new ApiResponse(false, "系统异常: " + e.getMessage(), null));
}

e.getMessage() 返回给前端。攻击者能从这里读到你的数据库连接串、表名、字段名。

Security Auditor 报告:

🟡 HIGH A09-LOGGING AuthController.java:47

Pattern: Exception message exposed in API response

Risk: Information disclosure — internal stack details visible to clients

Fix: Log exception server-side, return generic message to client

修复后:

代码语言:java
复制
} catch (Exception e) {
    log.error("Login failed for user: {}", request.getUsername(), e);
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
        .body(ApiResponse.error("服务暂时不可用,请稍后重试"));
}

漏洞4:密码强度没有限制(HIGH)

代码语言:java
复制
// RegisterRequest.java - BEFORE
public class RegisterRequest {
    @NotBlank
    private String username;
    
    @NotBlank
    @Size(min = 8, message = "密码至少8位")
    private String password;
    // 只检查了长度,没检查复杂度
}

Security Auditor 报告:

🟡 HIGH A07-AUTHN RegisterRequest.java:12

Pattern: Weak password policy — length check only

Risk: Weak passwords increase brute force attack surface

Fix: Add complexity requirements (uppercase, number, special char)

修复后:

代码语言:java
复制
public class RegisterRequest {
    @NotBlank
    @Size(min = 3, max = 20)
    @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只允许字母、数字、下划线")
    private String username;

    @NotBlank
    @Size(min = 8, max = 72)
    @Pattern(
        regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$",
        message = "密码需包含大小写字母、数字和特殊字符"
    )
    private String password;
}

漏洞5:个人中心接口缺少频率限制(MEDIUM)

代码语言:java
复制
// ProfileController.java - BEFORE
@PutMapping("/profile/{userId}")
@PreAuthorize("#userId == authentication.principal.id")
public UserProfileResponse updateProfile(@PathVariable Long userId,
                                         @RequestBody UpdateProfileRequest request) {
    // 没有频率限制,可以无限次调用
    return userService.updateProfile(userId, request);
}

Security Auditor 报告:

🟠 MEDIUM A04-INSECURE-DESIGN ProfileController.java:28

Pattern: No rate limiting on authenticated endpoints

Risk: Account enumeration and abuse via high-frequency requests

Fix: Add @RateLimiter or Bucket4j rate limiting

修复后(用 Bucket4j + Spring Boot):

代码语言:java
复制
@PutMapping("/profile/{userId}")
@PreAuthorize("#userId == authentication.principal.id")
public UserProfileResponse updateProfile(@PathVariable Long userId,
                                         @RequestBody @Valid UpdateProfileRequest request,
                                         HttpServletRequest httpRequest) {
    String clientIp = getClientIp(httpRequest);
    rateLimiter.checkLimit("profile-update:" + userId, 10, Duration.ofMinutes(1));
    return userService.updateProfile(userId, request);
}

Maven 依赖 CVE 扫描:Java 项目的专属环节

"先为不可胜,以待敌之可胜。" ——孙子《军形篇》

npm 项目有 npm audit,Java 项目对应的是 OWASP Dependency Check,Maven 插件一行配置:

代码语言:xml
复制
<!-- pom.xml -->
<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>9.0.9</version>
    <configuration>
        <failBuildOnCVSS>7</failBuildOnCVSS>  <!-- CVSS 7分以上阻断构建 -->
        <formats>
            <format>HTML</format>
            <format>JSON</format>
        </formats>
    </configuration>
</plugin>

CI 里加一步:

代码语言:yaml
复制
# .github/workflows/security.yml
- name: OWASP Dependency Check
  run: mvn dependency-check:check
  
- name: Upload Security Report
  uses: actions/upload-artifact@v4
  if: always()
  with:
    name: dependency-check-report
    path: target/dependency-check-report.html

Security Auditor 在会话内做代码逻辑扫描,Maven Dependency Check 在 CI 里做依赖 CVE 扫描。两个层面都要守。


3条洞见

洞见1:安全漏洞不是程序员的失误,是流程的缺失

老李那个登录 SQL 注入,不是小王水平差,是流程里没有安全检查这一关。有了 Security Auditor,这一关是自动化的,不依赖人记不记得。


洞见2:不同漏洞的修复成本差了10倍

这不是夸张。一个在开发阶段五分钟修好的 SQL 注入,一旦到了生产,就要加上:事件响应、数据排查、用户通知、可能的合规报告。

所以 Security Auditor 的价值不是"找出漏洞",是"在正确的阶段找出漏洞"。


洞见3:安全审查要融入开发节奏,而不是独立评审会

Security Auditor 做的是"安全左移"——把安全检查从测试阶段、上线前评审,往左推到开发过程里。每次写完功能,跑一遍,跟跑单元测试一样自然。

等安全问题到了上线评审会上再发现,那代价已经不一样了。


怎么在你的项目里用起来

"胜兵先胜而后求战,败兵先战而后求胜。" ——孙子《军形篇》

安装(Claude Code skill 方式):

代码语言:bash
复制
git clone https://github.com/wrsmith108/claude-skill-security-auditor.git \
  ~/.claude/skills/security-auditor

或用 alirezarezvani 的工程技能包(OWASP Top 10 覆盖更全):

代码语言:bash
复制
/plugin install skill-security-auditor@claude-code-skills

使用口令:

审计认证模块

security audit on the auth module

检查硬编码密钥

check for hardcoded secrets in the project

扫描依赖漏洞(Java 项目)

audit dependencies for CVEs

CI 集成:高危漏洞阻断构建

npx tsx scripts/index.ts --fail-on high

Java 项目配合 Maven 的完整设置:

代码语言:xml
复制
<!-- pom.xml -->
<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>9.0.9</version>
    <configuration>
        <failBuildOnCVSS>7</failBuildOnCVSS>
    </configuration>
</plugin>

已知风险豁免(需要审批记录):

代码语言:json
复制
{
  "exceptions": [
    {
      "id": "GHSA-xxxx-xxxx-xxxx",
      "reason": "该漏洞仅在服务端渲染场景触发,我们使用 API 模式不受影响",
      "expires": "2026-09-01",
      "approvedBy": "security-lead@company.com"
    }
  ]
}

方法论速查表

漏洞类型

OWASP 分类

Security Auditor 能发现

修复方向

SQL注入

A03

参数化查询 / JPA 方法命名

硬编码密钥

A02

环境变量 / Secrets Manager

异常信息泄露

A09

服务端日志 + 通用错误响应

弱密码策略

A07

Bean Validation 正则

缺少频率限制

A04

Bucket4j + Redis

依赖 CVE

A06

✅(配合 mvn)

升级依赖版本

JWT 弱算法

A02

换 RS256 或强化 secret

CORS 配置错误

A01

部分

明确 allowedOrigins

检查阶段

工具

覆盖范围

开发阶段

Security Auditor

代码逻辑漏洞、硬编码密钥

PR 阶段

人工 Review

业务逻辑安全、权限设计

CI 阶段

Maven dependency-check

依赖 CVE

生产运营

WAF + 渗透测试

运行时防护


事故处理完之后,老李把小王叫过来,给他看了那份安全报告。

不是为了追责。是为了让他明白一件事:Security Auditor 扫出来的那些漏洞,其实有一个共同的根源——代码太乱了,乱到连 Review 的人都没看出来。

SQL 注入那一行,藏在一个五十行的方法体里;硬编码的 JWT secret,混在一堆常量声明中间;异常信息泄露,包在三层 try-catch 里面。

老李指着其中一个问小王:"你 Review 的时候看到这里了吗?"

小王盯着屏幕,沉默了几秒。

"看到了。但没觉得有问题。"

这就是可读性和安全之间的关系——代码越乱,安全问题越容易被淹没。 清理可读性,不只是让代码好看,是让问题变得看得见。

老李说:"你先去把这套系统的代码用 Code Simplifier 过一遍,然后我们再重新跑一次 Security Auditor。"

小王有点没明白:"先清理可读性,再扫安全?"

"对。顺序很重要。"


下一篇:Code Simplifier——在安全扫描之前,先让代码变得可读。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 它是什么?一个在上线前替你过安全关的 Agent
  • 为什么 AI 生成的代码容易带安全漏洞?
  • 架构图:在Java+SpringBoot登录系统里的完整位置
  • 实战:老李的登录模块安全扫描
    • 漏洞1:SQL注入(CRITICAL)
    • 漏洞2:硬编码 JWT Secret(CRITICAL)
    • 漏洞3:异常信息泄露(HIGH)
    • 漏洞4:密码强度没有限制(HIGH)
    • 漏洞5:个人中心接口缺少频率限制(MEDIUM)
  • Maven 依赖 CVE 扫描:Java 项目的专属环节
  • 3条洞见
    • 洞见1:安全漏洞不是程序员的失误,是流程的缺失
    • 洞见2:不同漏洞的修复成本差了10倍
    • 洞见3:安全审查要融入开发节奏,而不是独立评审会
  • 怎么在你的项目里用起来
  • 审计认证模块
  • 检查硬编码密钥
  • 扫描依赖漏洞(Java 项目)
  • CI 集成:高危漏洞阻断构建
    • 方法论速查表
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档