MyBatis 主要通过 预编译机制 和 安全的参数绑定 防止 SQL 注入,同时需开发者遵循安全编码规范。以下是具体防护机制及最佳实践:
⚙️ 一、核心防注入机制
-
预编译语句(
#{}占位符)- 原理:MyBatis 默认使用 JDBC 的
PreparedStatement,将 SQL 语句与参数分离:- SQL 模板(如
SELECT * FROM users WHERE id = ?)先发送至数据库编译。举例:yczb.net.cn - 参数值通过
setXxx()方法独立传递,数据库将其视为纯数据(非代码),自动转义特殊字符(如单引号'→\')。
- SQL 模板(如
- 代码示例:
<select id="getUser" resultType="User"> SELECT * FROM users WHERE id = #{id} <!-- 安全 --> </select>yczb.net.cn - 对比
${}的风险:${}直接拼接字符串(如ORDER BY ${column}),若参数来自用户输入,攻击者可注入id; DROP TABLE users--等恶意代码。
- 原理:MyBatis 默认使用 JDBC 的
-
动态 SQL 标签的安全处理
MyBatis 的<if>、<where>、<foreach>等标签内部自动使用#{}机制,即使条件拼接也不会引入注入风险:<select id="searchUsers" resultType="User"> SELECT * FROM users <where> <if test="name != null"> <!-- 安全 --> AND name = #{name} </if> <if test="role != null"> AND role = #{role}www.yczb.net.cn </if> </where> </select>即使攻击者输入
name = 'admin' OR '1'='1',参数会被转义为普通字符串,无法改变 SQL 结构。示例:www.yczb.net.cn
⚠️ 二、${} 的高风险场景及安全使用
动态表名、列名等场景需使用 ${} 时,必须严格校验输入:
- 白名单校验:仅允许字母、数字、下划线等安全字符:
public String safeTableName(String input) { if (!input.matches("^[a-zA-Z0-9_]+$")) { throw new IllegalArgumentException("非法表名"); }m.yczb.net.cn return input; }<select id="queryByTable" resultType="map"> SELECT * FROM ${tableName} <!-- 确保 tableName 已校验 --> </select> - 禁止在注解中使用
${}:
表名若来自用户输入,可能导致注入。@Select("SELECT * FROM ${table} WHERE id = #{id}") // 危险! User getById(@Param("table") String table, @Param("id") Long id);
🛡️ 三、其他安全增强措施
- 输入参数过滤
- 业务层校验参数格式(如长度、字符集),拒绝非法输入(如使用正则
[a-zA-Z0-9]+过滤用户名)。
- 业务层校验参数格式(如长度、字符集),拒绝非法输入(如使用正则
- 最小权限原则
- 数据库账号仅授予必要权限(禁用
DROP、EXECUTE等危险操作)。示例:m.yczb.net.cn
- 数据库账号仅授予必要权限(禁用
- 特殊查询的安全处理
- LIKE 查询:避免直接拼接
%:SELECT * FROM users WHERE name LIKE CONCAT('%', #{keyword}, '%') - IN 查询:使用
<foreach>自动转义:SELECT * FROM users WHERE id IN <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach>m.yingchao.mo.cn
- LIKE 查询:避免直接拼接
❌ 四、开发者常见错误与规避建议
| 错误做法 | 风险 | 正确方案 |
|---|---|---|
使用 ${} 拼接用户输入 | SQL 注入(如 ' OR 1=1 --) | 优先用 #{},非用 ${} 则白名单校验 |
动态排序字段未校验(ORDER BY ${field}) | 注入或语法错误 | 校验字段名合法性 |
| 存储过程内部拼接未转义 SQL | 二次注入风险 | 避免拼接,或强制参数化 |
注解中使用 ${} | 注入绕过 | 改用 XML 配置 + 白名单 |
💎 总结:MyBatis 防注入最佳实践
- 默认使用
#{}:所有参数值传递均优先采用预编译占位符。 - 动态 SQL 标签:安全处理条件拼接(
<if>、<where>)。 -
${}严格受限:仅用于表名/列名等非用户输入场景,且需白名单校验。 - 输入过滤 + 权限控制:业务层校验 + 数据库最小权限。
- 日志与测试:启用 SQL 日志审查,定期用 OWASP ZAP 等工具扫描漏洞。
ℹ️ 通过结合预编译机制、严格的
${}管控和业务层校验,可构建稳固的 SQL 注入防护体系。开发者的规范使用是防御的关键!

1万+

被折叠的 条评论
为什么被折叠?



