直接在 HttpServletRequest 上字符串替换无效,因SQL注入根源是未参数化拼接而非字符本身;拦截应落在JDBC或ORM执行前一刻,如MyBatis Interceptor或Spring AOP切入execute方法。

为什么不能直接在 HttpServletRequest 上做字符串替换
很多开发者第一反应是写个 Filter,拿到 request.getParameter("xxx") 后用 String.replace() 干掉 '、or、union 这类关键词——这不仅无效,还可能破坏正常业务数据(比如用户昵称含 O'Reilly)。SQL 注入的根源不在“字符”,而在“未参数化拼接”。拦截层无法可靠识别哪些请求参数最终会进 SQL,更无法判断拼接逻辑是否安全。
真正该拦截的位置:JDBC 层或 ORM 框架入口
全局防御必须落在 SQL 执行前一刻。对 MyBatis 项目,优先使用 Interceptor 拦截 StatementHandler.prepare();对纯 JDBC,可包装 Connection 或 PreparedStatement。Spring Boot 用户更推荐用 @Aspect 切入 JdbcTemplate 或 NamedParameterJdbcTemplate 的 execute 方法。
- MyBatis 示例:在
intercept()中检查boundSql.getSql()是否含#{}外的变量引用(如${}),有则抛异常 - 避免扫描整个 SQL 字符串匹配关键词——误报高、绕过易(如用注释
/**/or/**/1=1) - 注意
PreparedStatement的setString()等方法本身不触发 SQL 解析,拦截点必须在execute*调用前
Filter 层能做的有限但有用的事
虽然不能防注入,但可在 Filter 中做三件实际有效的事:
- 记录所有含
select、insert、update、delete(忽略大小写)的请求参数值,用于审计溯源 - 对明确禁止的模式(如
1=1、sleep(、benchmark()做粗粒度拒绝,返回 400,不依赖正则全量扫描,只查关键位置(如参数开头/结尾) - 强制要求所有 JSON 请求体走
Content-Type: application/json,并在 Filter 中校验 JSON 结构合法性,防止通过畸形 JSON 绕过前端校验
最常被忽略的盲点:ORM 自定义查询和动态 SQL
开发者往往只盯住 Controller 层的 @RequestParam,却忘了 MyBatis @SelectProvider、JPA @Query(nativeQuery = true)、甚至 CriteriaBuilder 构造过程中拼接的字符串。这些地方一旦用了 String.format() 或 + 拼接用户输入,Filter 根本看不见。
立即学习“Java免费学习笔记(深入)”;
这类代码必须人工 Code Review,自动化工具很难覆盖。上线前跑一遍 grep -r "\$\{.*\}" src/main/java/ 和 grep -r "createQuery.*\".*+.*\"" src/main/java/ 是最低成本的兜底动作。


















