当MyBatis遇上SQL注入:Java程序员的防弹少年团养成记
引言:来自产品经理的夺命连环Call
"小王!用户说我们系统里出现了’删库到跑路大礼包’!"某个深夜,当你在用MyBatis写着CRUD时,突然发现黑客用' OR 1=1 --
就破解了你的防线。别慌!今天我们就来给MyBatis穿上一套钢铁侠战衣,让你的SQL语句从此刀枪不入!
一、MyBatis的"薛定谔防护"原理
1.1 #{}与${}的量子纠缠
<!-- 安全の波动拳 -->
<select id="findUser" parameterType="String">
SELECT * FROM users WHERE username = #{name}
</select>
<!-- 作死の咏唱咒语 -->
<select id="findUser" parameterType="String">
SELECT * FROM users WHERE username = '${name}'
</select>
这两兄弟的区别就像保险柜和透明塑料袋:#{}
会把参数变成预编译语句的占位符,而${}
直接拼接字符串,相当于在数据库门前跳脱衣舞。
二、MyBatis防注入实战宝典
2.1 基础防御:预编译の绝对领域
// 正确姿势(XML版)
<update id="updateEmail">
UPDATE users SET email = #{newEmail}
WHERE id = #{userId} AND password = #{pwd}
</update>
// MyBatis-Plus的优雅姿势
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername, inputName)
.eq(User::getPassword, inputPwd);
userMapper.selectOne(wrapper);
此时生成的SQL会自动变成:WHERE username = ? AND password = ?
,黑客的注入代码会被当成普通字符串处理。
2.2 动态SQL的防弹衣穿法
<!-- 安全の动态查询 -->
<select id="searchUsers">
SELECT * FROM users
<where>
<if test="name != null">
AND username = #{name}
</if>
<if test="age != null">
AND age > #{age}
</if>
</where>
</select>
<!-- 危险の作死示范 -->
<select id="dangerSearch">
SELECT * FROM users WHERE 1=1
<if test="condition != null">
AND ${condition} <!-- 这相当于给黑客发VIP通行证 -->
</if>
</select>
三、MyBatis Plus的钢铁防线
3.1 Wrapper的正确打开方式
// 防注入の黄金圣衣
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("username", userInput) // 自动参数化
.apply("date_format(create_time,'%Y-%m') = {0}", month)
.last("limit 10"); // .last()要慎用!
// 错误示范の自爆卡车
wrapper.eq("username", "'admin' or 1=1 -- "); // MyBatis会自动转义
wrapper.apply("username = " + userInput); // 拼接SQL等于裸奔
3.2 批量操作的防弹舱
// 安全の批量插入
List<User> userList = ...;
userService.saveBatch(userList); // 自动生成预编译语句
// 危险のSQL拼接模式
<insert id="batchInsert">
INSERT INTO users VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.age}) <!-- 正确使用#{} -->
</foreach>
</insert>
四、防御体系升级:五层护盾计划
4.1 输入过滤の安检门
// 参数消毒工具类
public class SqlFilter {
public static String clean(String input) {
return input.replaceAll("[';\\\\/]", "");
}
}
// 使用示例
wrapper.eq("username", SqlFilter.clean(rawInput));
4.2 权限控制の金库锁
- 数据库账号只给最小权限:读账号禁用DELETE/UPDATE
- 生产环境禁用MyBatis的SQL打印(防止泄露表结构)
4.3 监控の天网系统
<!-- 配置SQL监控 -->
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
</configuration>
推荐搭配Druid的WallFilter使用,能自动拦截危险SQL,像有个24小时值班的数据库保安。
五、黑客攻防真人秀:测试你的防线
5.1 注入测试靶场
// 测试用例1:万能密码攻击
String username = "' or 1=1 -- ";
String password = "随便";
userMapper.selectOne(new QueryWrapper<User>()
.eq("username", username)
.eq("password", password));
// 预期结果:返回null,因为MyBatis会把整个字符串当用户名查询
// 测试用例2:时间盲注探测
String input = "1' AND (SELECT * FROM (SELECT SLEEP(5))test) -- ";
userMapper.selectById(input); // 应该立即返回,不会被延迟
5.2 安全扫描工具推荐
- SQLMap:在测试环境验证防护效果
- IDEA插件:Alibaba Java Coding Guidelines(自动检测${}滥用)
结语:让MyBatis成为黑客的叹息之墙
记住三大护法口诀:
预编译是亲爹,${}是魔鬼
Wrapper保平安,Plus更无敌
权限最小化,监控不能停
当你在MyBatis的XML里写下每一个#{}时,就是在给黑客的键盘上浇混凝土。现在就去检查你的项目,别让明天的晨会成为"删库事件复盘会"!
程序员与黑客的斗舞时刻
你的MyBatis项目踩过哪些注入坑?欢迎在评论区Battle你的防御绝招!点赞过100,下期揭秘《MyBatis Plus的花式防暴走指南》! 🔥🚀👨💻