filter不能真正拦截sql注入,因其无法获取最终执行的sql;它仅能记录输入、轻量校验格式,并需配合contentcachingrequestwrapper处理json等请求体,而动态sql漏洞必须通过代码审查和自动化扫描解决。

Filter 不能真正拦截 SQL 注入,它连 SQL 长什么样都看不到;能做的只是记录、粗筛、校验输入格式——真要防注入,必须在 MyBatis Interceptor 或 JdbcTemplate 执行前一刻下手。
为什么直接在 Filter 里 replace() 或正则匹配关键词是错的
常见错误现象:把用户昵称 O'Reilly 变成 OReilly,JSON 字段 {"type": "SELECT"} 解析失败,或放行了 sel/**/ect 这类绕过写法。
根本原因在于:SQL 注入不是“有单引号就危险”,而是“用户输入被拼进未参数化的 SQL 字符串”。Filter 拿不到最终执行的 SQL,也分不清哪个参数会进 DAO 层。
- 不要用
String.replace("'", "''")或删掉;—— 破坏业务数据,且对 PreparedStatement 无效 - 避免全量正则扫描整个参数值,比如
Pattern.compile("union|select", Pattern.CASE_INSENSITIVE)—— 误报高、易绕过 - 不处理
@RequestBody的 JSON 请求体?那等于漏掉一半攻击面(得用ContentCachingRequestWrapper缓存流) - 忽略字符编码:前端用
GBK发送%A3%5C,你用UTF-8解码就失效
Filter 能做且值得做的三件事
虽然不能防注入,但它是审计链的第一环,必须覆盖所有输入源(query string、form data、JSON body、multipart、header)。
- 记录所有含
select、insert、update、delete(忽略大小写)的请求参数值,带上traceId和时间戳,用于事后溯源 - 对参数开头/结尾做轻量检查:匹配
1=1、sleep(、benchmark(、||等极低误报率模式,命中即response.sendError(400) - 强制校验
Content-Type: application/json请求体:用ObjectMapper.readTree()尝试解析,失败则拒收——防止畸形 JSON 绕过前端校验
如何安全读取并检查 JSON 请求体
直接调用 request.getInputStream() 一次后,Controller 的 @RequestBody 就会读到空内容。必须包装缓存。
- 继承
ContentCachingRequestWrapper,在doFilter()中提前读取并缓存原始 body - 用
ObjectMapper.readTree()解析 JSON 后,递归遍历所有字符串字段,对每个值做轻量模式检查(只查开头/结尾,不全文扫) - 对
multipart/form-data,调用request.getParts(),逐个检查Part的内容类型和字符串值 - 别忘了 GET 请求的 query string,它和表单一样走
getParameterMap(),但需手动 URL 解码(URLDecoder.decode(str, request.getCharacterEncoding()))
最常被忽略的盲点:动态 SQL 和 nativeQuery
Filter 根本看不见这些代码:
-
@SelectProvider方法里用StringBuilder拼接用户输入 -
@Query(nativeQuery = true)中硬编码了"SELECT * FROM user WHERE name = '" + name + "'" -
CriteriaBuilder构造过程中调用String.format()
这类问题只能靠人工 Code Review + 自动化脚本扫描(如 grep @SelectProvider\|@Query.*nativeQuery),Filter 层完全无能为力。这也是为什么团队总在渗透测试后才“紧急加 Filter”——补的只是日志,不是漏洞。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











