java 17防sql注入的核心是所有用户输入必须通过预编译机制(如preparedstatement或mybatis的#{})处理,严禁字符串拼接;框架不自动免疫,orm、hql、原生sql均需遵守参数绑定规则,过滤器仅为补丁而非防线。

Java 17项目中防SQL注入,核心就一条:所有用户输入的值类参数,必须走预编译机制,绝不能拼接进SQL字符串——无论是用JDBC原生写法,还是MyBatis、Hibernate等框架,这条红线不能破。
PreparedStatement 是 Java 17 中最可靠、最无脑的安全基线
Java 17 仍完全兼容 JDBC 4.3 规范,PreparedStatement 的行为与早期版本一致,但类型推导和资源自动管理(try-with-resources)更稳妥。它不是“可选优化”,而是唯一能从执行模型上切断注入路径的方式。
- 只要 SQL 模板固定、参数用
setString()/setLong()等方法填入,数据库驱动就会把值当纯数据处理,单引号、分号、OR 1=1全部失效 - 别用
Statement+String.format()或+拼接,哪怕你“确认输入是数字”——类型校验在应用层,而注入发生在SQL解析阶段 - 注意
setNull(int, int)的第二个参数必须是JDBC类型常量(如Types.VARCHAR),否则可能触发空指针或类型不匹配异常
MyBatis 中 #{} 和 ${} 的边界必须刻在脑子里
#{} 是 Java 17 项目里 MyBatis 的默认安全开关;${} 是手动开的后门,只在白名单校验后才允许动。
-
WHERE name = #{name}→ 安全,转为WHERE name = ?,由 JDBC 驱动加引号并转义 -
ORDER BY ${sortField}→ 危险!必须先校验sortField是否属于Set.of("id", "created_at", "status"),否则攻击者可传入id; DROP TABLE users-- -
LIKE '%${keyword}%'→ 绝对禁止!正确做法是 Java 层拼好通配符:mapper.search("%" + keyword + "%"),XML 中仍写username LIKE #{keyword} -
IN查询必须用<foreach></foreach>标签,不能写id IN (${ids})或id IN (#{ids})(后者会把整个 List 当一个 Object 传,报类型错误)
ORM 框架不等于自动免疫,HQL/JPQL 也得守规则
Hibernate 在 Java 17 下默认启用 JPA 3.1,但它的防护只覆盖命名参数(:name)和位置参数(?1),一旦混用字符串拼接,照样崩。
- 写 HQL 时用
query.setParameter("name", username),别写"WHERE name = '" + username + "'" - 原生 SQL 查询(
@Query(nativeQuery = true))必须搭配setParameter(),Spring Data JPA 不会自动帮你转义 - Criteria API 虽然类型安全,但若手写
cb.equal(root.get("name"), "'" + username + "'"),等于自己造漏洞 - 避免在实体字段上用
@Formula引入动态 SQL 片段,这类表达式不经过参数绑定
过滤器和正则拦截只是补丁,不是替代方案
全局 Filter 对 SELECT、UNION、EXEC 等关键字做请求级拦截,听着很猛,但实际漏报率高、误杀率也高——比如正常字段名含 user_name 就可能被干掉。
- 它无法防御基于布尔盲注或时间盲注的攻击(不带关键字)
- URL 参数、Header、JSON Body 都要单独适配,维护成本陡增
- 一旦某处用了
multipart/form-data或 Base64 编码,过滤器大概率失效 - 真正该花精力的地方,是统一收口 DAO 层,强制所有查询走
PreparedStatement或#{},而不是在入口堆正则
Java 17 本身不新增 SQL 注入防护能力,它只是让旧机制运行得更稳;真正的防线不在 JDK 版本,而在每一行 SQL 构建逻辑是否拒绝拼接、每一个参数是否经过预编译通道——这点,从 Java 5 到 Java 21 都没变过。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











