Java代码审计实战:从SQL注入到权限控制

发布时间:2026/10/26 5:42:57
Java代码审计实战:从SQL注入到权限控制

1. 代码审计的本质与常见误区

刚入行那会儿,每次听到"代码审计"这个词,总觉得是安全专家才能干的高端活。直到自己完整走完几个项目的审计流程才发现,这其实就是程序员日常开发的延伸技能。代码审计的核心,无非是用开发者的视角去发现那些可能被恶意利用的编码漏洞。

最常见的误区是把代码审计等同于渗透测试。实际上,前者是静态分析(不运行代码),后者是动态测试。举个例子:审计时发现MyBatis中使用了${}拼接SQL,就能直接判定存在注入风险,而不需要真的构造攻击参数。

另一个误区是认为只有安全团队才需要关注审计。其实在Spring Boot项目的CRUD开发中,程序员每写一个Repository接口,本质上都在做微观层面的代码审计。区别只是专业审计会更系统化,会建立完整的漏洞检查清单。

2. Java项目审计的核心检查点

2.1 SQL注入的典型场景

在Java生态中,SQL注入主要出现在三个层面:

  1. 原生JDBC:最经典的拼接漏洞
// 高危写法 String sql = "SELECT * FROM users WHERE name='" + name + "'";
  1. ORM框架:
  • MyBatis中使用${}而非#{}(动态SQL场景尤其要注意)
  • JPA的@Query注解拼接字符串
  1. 存储过程调用:CallableStatement参数未过滤

经验:用阿里规约插件扫描时,所有"PreparedStatement未使用参数化查询"的警告都必须当作P0问题处理

2.2 权限控制漏洞

Spring Security配置不当会导致:

  • 鉴权注解缺失:@PreAuthorize漏写
  • CSRF防护关闭:.csrf().disable()出现在生产配置
  • 接口路径通配:/api/**这样的宽松匹配

最近审计的一个案例:开发者在Controller写了@Secured("ROLE_ADMIN"),但Spring Security配置里根本没启用注解驱动:

@Configuration @EnableWebSecurity public class SecurityConfig { // 缺失@EnableGlobalMethodSecurity }

2.3 反序列化风险

Fastjson、Jackson这些库的漏洞常被忽视。关键检查项:

ObjectMapper mapper = new ObjectMapper(); // 高危配置 mapper.enableDefaultTyping();

建议的防御配置:

mapper.activateDefaultTyping( LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY );

3. 企业级审计实战流程

3.1 准备工作清单

  1. 环境搭建:
  • JD-GUI/IDEA反编译插件(审计第三方jar)
  • OWASP ZAP/SonarQube(基础扫描)
  • Git版本对比工具(看历史修复记录)
  1. 审计范围确认:
graph TD A[全部代码] -->|大型项目| B(采样审计) A -->|关键系统| C(全量审计) B --> D(核心业务模块+公共组件) C --> E(包含测试代码)
  1. 规则定制: 根据公司技术栈调整检查项,比如:
  • 使用MyBatis的项目:重点检查XML映射文件
  • 用Redis缓存的:检查序列化方式

3.2 人工审计四步法

  1. 入口定位:
  • 从Controller层向下追踪
  • 特别关注@RequestMapping中的路径参数
@GetMapping("/user/{id}") public User getUser(@PathVariable String id) { // 未做正则校验的路径变量可能引发注入 }
  1. 数据流分析: 画出入参的完整传递路径,重点检查:
  • 多层DTO转换时的数据净化
  • 跨微服务调用时的参数传递
  1. 上下文验证: 同一个SQL查询在不同业务场景下风险等级不同。例如查询日志和查询支付记录的安全要求完全不同。

  2. 修复方案设计: 不是所有漏洞都要大改架构。比如旧系统暂时无法重构时,可以用Hibernate Validator做应急防护:

@Pattern(regexp = "^[a-zA-Z0-9_]+$") private String username;

4. 高频漏洞修复示范

4.1 MyBatis动态SQL改造

问题代码:

<select id="findUsers" resultType="User"> SELECT * FROM users WHERE name LIKE '%${name}%' </select>

修复方案:

<select id="findUsers" resultType="User"> SELECT * FROM users WHERE name LIKE CONCAT('%',#{name},'%') </select>

特殊情况处理:当必须使用${}时(如动态表名),必须增加白名单校验:

// 校验逻辑示例 private static final Set<String> ALLOWED_TABLES = Set.of("users","products"); public String filterTableName(String input) { if(!ALLOWED_TABLES.contains(input.toLowerCase())){ throw new IllegalArgumentException("Invalid table name"); } return input; }

4.2 Spring Security加固

典型弱配置:

http.authorizeRequests() .antMatchers("/admin/**").authenticated() .anyRequest().permitAll();

强化方案:

http.authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/api/**").authenticated() .anyRequest().denyAll() // 默认拒绝所有 .and() .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

5. 自动化审计工具链

5.1 开源工具组合

  1. 静态扫描:
  • SpotBugs:检测基础编码缺陷
  • PMD:自定义规则检查
  • 阿里规约插件:中文团队友好
  1. 依赖检查:
  • OWASP DependencyCheck
  • Snyk CLI
  1. IAST工具:
  • Contrast Community Edition
  • Hdiv Detection

5.2 自建规则示例

用ArchUnit做架构约束测试:

@ArchTest static final ArchRule no_jdbc_direct_call = noClasses().should().dependOnClassesThat() .resideInAnyPackage("java.sql..");

用GitHook做提交拦截:

#!/bin/sh # pre-commit hook mvn spotbugs:check if [ $? -ne 0 ]; then echo "静态检查未通过!" exit 1 fi

6. 审计报告编写要点

  1. 风险分级模板: | 等级 | 标准 | 修复时限 | |------|------|----------| | 紧急 | 可直接导致数据泄露 | 24小时 | | 高危 | 需特定条件触发 | 1周 | | 中危 | 影响范围有限 | 1个月 |

  2. 修复建议写法: 错误示范:"建议修复SQL注入" 正确写法: "在UserMapper.xml第47行,将${searchKey}替换为#{searchKey},并在Service层添加长度校验:

if(searchKey.length() > 100){ throw new InvalidParameterException(); } ```" 3. **证据留存**: - 漏洞代码截图(含行号) - 请求样本(如有) - 调用栈信息 ## 7. 开发者自检清单 每天提交代码前快速检查: 1. [ ] 所有用户输入是否经过校验? 2. [ ] 数据库查询是否使用参数化? 3. [ ] 配置文件是否包含敏感信息? 4. [ ] 新接口是否添加权限注解? 5. [ ] 第三方库版本是否已扫描? 对于核心业务模块,建议额外检查: - 资金计算是否用BigDecimal - 批量操作是否加锁 - 日志是否记录关键操作 ## 8. 企业级实践建议 1. **流程嵌入**: - 代码评审模板中加入安全检查项 - CI流水线加入安全扫描阶段 - 上线前必须完成审计报告 2. **知识沉淀**: - 建立内部漏洞案例库 - 编写框架安全编码规范 - 定期做漏洞复盘会 3. **红蓝对抗**: - 每季度组织内部攻防演练 - 对暴露的问题做根本原因分析 - 重要系统安排专项审计 在金融项目实践中我们发现,80%的安全问题可以通过基础编码规范避免。最近推动团队在IDEA里配置了实时检测模板,新人提交SQL注入漏洞的几率直接下降了60%。这比事后审计效率高得多。