框架升级不能修复SQL注入漏洞,必须将代码中所有${}替换为#{}或参数化查询,并校验动态SQL的合法性。

SQL注入没被修复,只升级框架版本根本没用
这里有个常见的误区,以为把 MyBatis 从 3.4.6 升级到 3.5.10,或者把 Spring JDBC 从 5.2.3 升到 6.1.0,就能高枕无忧了。事实恰恰相反:框架升级本身,并不会自动修复你代码里已经存在的SQL注入漏洞。它顶多能堵住框架自身代码库里的已知安全缺陷,比如某个特定版本里 SqlSessionTemplate 的 selectList 方法对 ${} 的处理有问题。但问题在于,你亲手写的那些 SELECT * FROM user WHERE name = '${name}' 代码,无论框架版本新旧,都会原封不动地执行——漏洞的根源在你自己的业务逻辑里,而不在框架的jar包中。
真正要改的是 SQL 拼接方式:${} 必须换成 #{} 或参数化查询
核心区别必须搞清楚:${} 是简单的字符串替换,而 #{} 才是预编译占位符。只要代码里还在用 ${} 去拼接表名、字段名或者 ORDER BY 子句,就等于把用户输入的数据直接丢给了数据库解析器,风险敞口大开。
- 动态表名怎么办? 正确的做法是先用白名单校验,校验通过后再进行拼接,绝对不要直接使用
${tableName}。 - 动态排序字段呢? 同样,只允许使用预定义列表(比如
['id', 'created_time', 'status'])中的值。在MyBatis中可以用ORDER BY #{sortField},或者在Ja va层校验后,用String.format("ORDER BY %s", whiteListedField)的方式安全拼接。 - 条件模糊查询,像
where name like '%${keyword}%'这种写法是绝对禁止的。必须改成WHERE name LIKE CONCAT('%', #{keyword}, '%'),让数据库引擎自己去处理字符串拼接。 - 如果是JDBC原生写法,那就必须彻底弃用
Statement,统一改用PreparedStatement,并通过setString()这类方法安全地传入参数。
升级框架前先 grep 出所有 ${} 和 Statement.execute*
在动手升级之前,不把代码仓库彻底扫一遍,你根本分不清哪些 ${} 是真正必要的动态SQL,哪些只是历史遗留的“假动态”(比如写死的 ${@table_prefix}user)。盲目升级反而可能因为新版本框架规则更严格而直接报错,暴露出更多隐藏的问题。
- 首先,运行命令
grep -r '\$\{.*\}' src/main/ja va/ --include="*.xml" --include="*.ja va",把所有的风险点都揪出来。 - 接着,仔细检查所有
Statement.execute*、executeUpdate、executeQuery的调用,确认传入的是否是拼接好的字符串。 - 要特别留意那些日志埋点、SQL导出功能、调试工具类——这些地方最容易为了图方便,偷偷用字符串拼接的方式生成SQL。
- 对于MyBatis中
标签块里混用${}和#{}的情况,也需要逐行审查,确保安全。
框架升级本身有兼容性雷区:Mapper 接口和 XML 易失效
框架升级可不是点一下按钮就完事了,它本身自带一系列兼容性“雷区”。例如,MyBatis 3.5+ 版本对 @SelectProvider 返回值类型的推断更加严格;Spring Boot 3.x 则要求搭配 MyBatis 3.5.13+,并且默认禁用了 auto-mapping-unknown-column-beha vior 配置,老项目一升级就可能查不到字段。
- 在XML映射文件中,如果
使用了autoMapping="true",升级后可能导致字段映射失败。稳妥起见,建议显式地写全所有。 - 自定义的
TypeHandler如果继承了过时的抽象类(比如旧的BaseTypeHandler),可能需要重写setNonNullParameter和getNullableResult的多个重载方法。 - 从Spring Boot 2.7升级到3.0后,
mybatis-spring-boot-starter的配置项前缀从mybatis.变成了mybatis.configuration.,漏改会导致配置完全失效。 - 升级完成后,务必跑一遍单元测试,重点观察
MapperTest是否抛出BindingException或者返回空结果——这通常意味着XML文件路径没被正确扫描到classpath中,或者命名空间写错了。
说到底,最麻烦的往往不是升级这个动作本身,而是后续的确认工作:你需要逐一核实每一处 ${} 的使用是否真的必要、前端校验有没有被绕过、中间件(比如API网关)会不会悄悄注入额外参数。这些问题,靠单纯提升框架版本号是解决不了的。
