mybatis中误用${}导致sql注入,因其绕过预编译直接拼接用户输入;动态排序、表名等必须白名单校验;jdbc需用preparedstatement;注解sql同理;xml配置须保密凭证并禁用危险参数。

MyBatis XML中误用${}导致SQL注入
直接在MyBatis的<select></select>或<update></update>标签里写${username},等同于把用户输入原样拼进SQL——不经过预编译,也不做任何转义。攻击者传入' OR 1=1 --,就能绕过条件限制查出全部数据。
常见错误场景包括:动态排序字段(ORDER BY ${sortField})、动态表名(FROM ${tableName})、拼接WHERE子句(WHERE status = ${status})。
- 只要参数来自不可信输入(如HTTP请求、日志解析、MQ消息),就绝不能用
${} - 若必须动态指定列名/表名,需严格白名单校验:
if (!Arrays.asList("name", "email", "created_time").contains(sortField)) throw new IllegalArgumentException(); - MyBatis日志开启后,可在
application.yml里加logging.level.org.apache.ibatis=DEBUG,确认最终执行的SQL是否含未处理的变量
JDBC原始代码里字符串拼接SQL
手写Statement并用+拼接用户输入,是最典型的漏洞源头。比如"SELECT * FROM users WHERE id = " + request.getParameter("id")——整条SQL在运行时才组装,数据库引擎会把它当完整语句解析执行。
这种写法在工具类、批处理脚本或遗留系统中仍存在,风险极高且难以被静态扫描覆盖。
- 必须改用
PreparedStatement,占位符?只接受值绑定,不参与语法解析 -
setString()、setInt()等方法会自动处理引号、转义,无需手动加单引号 - 注意
PreparedStatement不能用于表名、列名、ORDER BY子句——这些属于SQL结构,不是“数据”,得靠白名单+字符串拼接(仅限可信来源)
MyBatis注解方式下硬编码SQL含${}
用@Select("SELECT * FROM users WHERE name = ${name}")和XML里写${}本质相同,都是绕过MyBatis的预编译机制。IDE可能不报错,但运行时毫无防护。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
尤其容易出现在快速原型开发或测试代码中,开发者为图方便直接内联SQL,忽略安全边界。
- 一律替换为
@Select("SELECT * FROM users WHERE name = #{name}") - 若注解里需动态字段(如
GROUP BY ${groupField}),先校验groupField是否在允许列表内,再拼接 - 禁用
org.apache.ibatis.scripting.xmltags.DynamicSqlSource以外的动态SQL生成方式,避免反射调用触发非预期行为
Spring XML配置文件中暴露JDBC连接参数
Spring旧式XML配置(如applicationContext.xml)若明文写jdbc:mysql://localhost:3306/db?user=root&password=123456,虽不直接导致SQL注入,但泄露数据库凭证后,攻击者可直连执行任意语句——相当于绕过所有应用层防护。
更隐蔽的风险是<bean class="org.springframework.jdbc.core.JdbcTemplate"></bean>未配置setDataSource,导致底层复用全局连接池,一旦某处SQL被注入,影响范围扩大。
- 数据库密码必须从环境变量或密钥管理服务读取,XML中只留占位符:
${DB_PASSWORD} - 禁用
allowUrlInFile等MySQL驱动危险参数,防止LOAD_FILE()类攻击利用 - 检查
DriverManagerDataSource是否被误用——它不支持连接池,且无法隔离不同DAO的SQL执行上下文
实际修复中最容易被忽略的是:动态SQL中混用#{}和${}时,以为“只有一处用${}没关系”。但只要有一个${}没校验,整条SQL就失去防护意义。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










