真正防御sql注入必须在mybatis interceptor的statementhandler.prepare()处拦截并检查${},配合jdbctemplate切面审计及人工排查动态sql;filter仅能做日志记录、轻量模式匹配和json校验。

直接在 HttpServletRequest 层做字符串替换式过滤,无法真正防御 SQL 注入——它既拦不住合法含关键词的业务数据(比如用户名 O'Reilly),也发现不了 ORM 动态拼接、@Query(nativeQuery = true)、@SelectProvider 等真正高危路径。
Filter 层能做的三件实际有效的事
虽然不能替代参数化查询,但 Filter 是审计与粗筛的第一道防线:
- 记录所有含
select、insert、update、delete(忽略大小写)的请求参数值,打上时间戳和 traceId,用于事后溯源 - 对参数开头/结尾做轻量检查:匹配
1=1、sleep(、benchmark(、||等极低误报率的模式,命中即返回400,不扫描整段内容 - 强制校验
Content-Type: application/json请求体的 JSON 合法性(用ObjectMapper.readTree()尝试解析),防止通过畸形 JSON 绕过前端校验
MyBatis 项目必须用 Interceptor 拦在 execute 前
这是唯一能覆盖 #{} 和 ${} 差异的可靠位置:
- 在
intercept()中调用boundSql.getSql(),检查是否含未被#{}包裹的变量引用(即存在${xxx}) - 禁止全量正则扫描 SQL 字符串——攻击者可用
sel/**/ect或注释绕过 - 若检测到
${}且其值来自用户输入(需结合自定义注解或白名单字段判断),直接抛IllegalArgumentException - 注意:拦截点必须是
StatementHandler.prepare(),不是setString()——后者不触发 SQL 解析
Spring Boot 用户优先用 @Aspect 切 JdbcTemplate
比 Filter 更靠近执行层,且能覆盖 DAO 方法调用:
- 切点写成
@Around("execution(* org.springframework.jdbc.core.JdbcTemplate.execute*(..))") - 从
ProceedingJoinPoint提取第一个参数(通常是String sql或PreparedStatementCreator),检查原始 SQL 是否含危险模式 - 对
NamedParameterJdbcTemplate同理,但要先展开:param占位符再检查(仅用于审计,不建议阻断) - 避免切
DataSource.getConnection()——太底层,会干扰连接池健康检测
最常被忽略的盲点:动态 SQL 和 nativeQuery
这类代码根本不会经过 Filter 或 Interceptor,只能靠人工和脚本兜底:
-
grep -r "\$\{.*\}" src/main/java/找所有 MyBatis 的${}使用点 -
grep -r "createQuery.*\".*+.*\"" src/main/java/找 JPA CriteriaBuilder 或原生字符串拼接 - 重点 review
@SelectProvider、@Query(nativeQuery = true)、SQLUtils.buildXXX()类方法
上线前漏掉一个 String.format("SELECT * FROM %s WHERE id = %d", tableName, userId),整个过滤器体系就形同虚设。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











