MyBatis 框架:被过度信任的 "老朋友"?
MyBatis,这名字在 Java 圈子里简直如雷贯耳。它号称简化数据库操作,让开发者能够通过 XML 映射或者注解,把 Java 对象和数据库里的数据 "勾搭" 在一起。跟 Hibernate 这种 "傻瓜式" ORM 框架不同,MyBatis 允许咱们自己手写 SQL 语句,这意味着什么?意味着更灵活,但也意味着...更大的风险!毕竟,自由过了头,容易翻车。现在,谁还没用过 MyBatis 呢?但你真的吃透它了吗?
MyBatis 的基本套路,说白了就三板斧:
- XML 映射文件: 告诉 MyBatis,你的 SQL 语句长啥样,参数怎么传。
- Mapper 接口: 定义一些 Java 方法,对应 XML 里的 SQL 语句。
- 代码执行: 调用 Mapper 接口的方法,MyBatis 就会帮你执行 SQL,把结果 "吐" 出来。
这些基础操作,随便找个教程都能学会。但今天咱们要聊点 "刺激" 的。
MyBatis:看似安全的 "外衣",藏不住的 SQL 注入?
MyBatis 在处理 SQL 语句的时候,提供了两种参数占位符:${}
和 #{}
。
#{}
这种预编译的方式,通常被认为是安全的,能有效防止 SQL 注入。但真相是这样吗?未必!
${}
这种方式,就是赤裸裸的字符串拼接,跟传统的 SQL 注入没啥区别。
<select id="selectC" parameterType="String" resultType="com.demo.bean.User">
select * from sqldemo.user where name = '${name}'
</select>
如果 name
传入 'abcde'
, MyBatis 就会直接把 SQL 拼接成这样:
select * from sqldemo.user where name = 'abcde'
然后丢给数据库执行。这...简直就是把 "SQL 注入" 四个字写在脸上了!
再看看 #{}
, 它的 "预编译" 机制,按理说应该能 "免疫" SQL 注入。
<select id="selectC" parameterType="String" resultType="com.demo.bean.User">
select * from sqldemo.user where name = #{name}
</select>
当 name
传入 'abcde'
, MyBatis 会先把 SQL 变成这样:
select * from sqldemo.user where name = ?
然后用参数化的方式,把 'abcde'
传给数据库。这样一来,单引号什么的,就都成了普通字符,没法 "兴风作浪" 了。
所以,MyBatis 导致 SQL 注入的 "罪魁祸首",其实就是 ${}
。但问题来了,如果非要用 ${}
,又该如何 "优雅" 地判断是否存在注入点呢?
别再傻傻地用单引号了!MyBatis 注入检测的 "正确姿势"
传统的 SQL 注入检测,通常会用到单引号。一个单引号报错,两个单引号正常,基本就能判断存在字符型 SQL 注入了。
<select id="selectC" parameterType="String" resultType="com.demo.bean.User">
select * from sqldemo.user where name = '${name}'
</select>
如果传入一个单引号,看到报错:
然后传入两个单引号,发现页面正常:
恭喜你,找到一个 SQL 注入点!
但现实往往更残酷。在实战中,你可能会遇到各种各样的 WAF(Web 应用防火墙)。一个单引号直接拦截,两个单引号也一样。这可咋办?难道就束手就擒了吗?
No!作为一名合格的网络安全工程师,我们必须 "搞事情"!
庖丁解牛:MyBatis 的 "内心世界"
想要绕过 WAF,我们需要更深入地了解 MyBatis 的工作原理。
如果 ${}
和 #{}
同时出现在一个 SQL 语句中,MyBatis 会怎么处理?
<select id="selectC" parameterType="String" resultType="com.demo.bean.User">
select * from sqldemo.user where name = #{name} or name = '${name}'
</select>
你可能会觉得,MyBatis 会按照从左到右的顺序,先替换 #{name}
为 ?
,再替换 ${name}
为具体数值。但实际上,MyBatis 的解析顺序是这样的:
- 优先替换
${}
中的内容。
从下面的调试信息可以看出,MyBatis 会先找到 ${
字符的位置:
然后把 ${
到 }
之间的数据,替换成传入的值。假设我们传入 bob
,那么 SQL 语句就会变成这样:
select * from sqldemo.user where name = #{name} or name = 'bob'
- 替换完所有
${}
之后,再去解析#{}
。
接下来,MyBatis 会解析 #{
字符:
此时,#{
位置会被替换成 ?
预编译字符:
最终,SQL 语句会变成这样:
select * from sqldemo.user where name = ? or name = 'bob'
- MyBatis 带着 SQL 语句和参数,去数据库查询数据。
看到这里,你可能会觉得 "so what?",但这正是突破 WAF 的关键!
"参数绑定":MyBatis 的另一张 "面孔"
在 MyBatis 中,参数绑定是一个非常重要的概念。
<select id="select" parameterType="String" resultType="com.demo.bean.User">
select * from sqldemo.user where id = '${id}' or name = #{name}
</select>
如果 SQL 语句中需要解析多个参数,比如 ${id}
和 #{name}
,那么就需要在 Mapper 接口中进行参数绑定:
List<User> select(@Param(value="id") String id, @Param(value="name") String name);
这样,MyBatis 才能知道 ${id}
和 #{name}
的数据从哪里来。
但如果我们在 SQL 映射文件中,使用了一个没有绑定过的参数名,比如 #{test}
,会发生什么?
MyBatis 会抛出一个错误:Parameter 'test' not found
。
记住这个报错信息,接下来就要用到它了!
不用单引号,也能 "搞定" MyBatis 注入点
现在,我们把之前学到的知识 "串" 起来。
<select id="selectC" parameterType="String" resultType="com.demo.bean.User">
select * from sqldemo.user where id = #{id} and name = '${name}'
</select>
如果我们将 name
的值设置为 #{name}
,会发生什么?
你会发现,MyBatis 报错了!这是因为:
-
MyBatis 先解析
${}
,把#{name}
替换到 SQL 语句中:sql select * from sqldemo.user where id = #{id} and name = '#{name}'
-
然后解析
#{}
,把#{id}
和#{name}
都替换成?
预编译字符:sql select * from sqldemo.user where id = ? and name = '?'
由于预编译符号的数量和参数不匹配,导致了错误。
但是,在实际环境中,我们并不能确定参数名就是 name
。没关系,我们可以使用任意不存在的参数名,比如 #{test}
。
这样,就会出现之前提到的 "参数未发现" 错误:
关键在于,通过对比不同的报错信息,我们就能判断出是否存在注入点!
当我们给 name
赋值为 #{test}
和 #test}
时,产生的报错信息是不同的:
#{test}
会触发 "参数未发现" 错误,而 #test}
则不会。这说明 name
参数位置使用了 ${}
,因此必定存在 SQL 注入!
到这里,我们已经成功绕过 WAF,找到了 MyBatis 的注入点!
为了方便大家使用,我已经把这种方法集成到了 xiasql 工具中。
还有 "盲点"?数字型和排序型注入的 "骚操作"
但是,还有两种情况需要考虑:
-
数字型注入:
xml <select id="selectA" parameterType="String" resultType="com.demo.bean.User"> select * from sqldemo.user where id = ${id} </select>
-
排序型注入:
xml <select id="selectB" parameterType="String" resultType="com.demo.bean.User"> select * from sqldemo.user where id = #{id} order by ${sort} </select>
这两种情况下,都没有单引号的限制。难道要用传统的 SQL 注入方式来判断吗?
当然也可以,但我们可以用更 "优雅" 的方式。
直接上 POC:
/*#xxxx}*/ /*#{xxxx}*/
学会这招,你就偷着乐吧!
黑客/网络安全学习包
资料目录
-
成长路线图&学习规划
-
配套视频教程
-
SRC&黑客文籍
-
护网行动资料
-
黑客必读书单
-
面试题合集
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
*************************************CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************
1.成长路线图&学习规划
要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
*************************************CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************
2.视频教程
很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,其中一共有21个章节,每个章节都是当前板块的精华浓缩。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
*************************************CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*************************************
3.SRC&黑客文籍
大家最喜欢也是最关心的SRC技术文籍&黑客技术也有收录
SRC技术文籍:
黑客资料由于是敏感资源,这里不能直接展示哦!
4.护网行动资料
其中关于HW护网行动,也准备了对应的资料,这些内容可相当于比赛的金手指!
5.黑客必读书单
**
**
6.面试题合集
当你自学到这里,你就要开始思考找工作的事情了,而工作绕不开的就是真题和面试题。
更多内容为防止和谐,可以扫描获取~
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
*************************************CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享*********************************