type
Post
status
Published
date
slug
summary
tags
CTF
推荐
category
CTF-Knowledge
category (1)
icon
password
comment
动态SQL
动态 SQL 是 MyBatis 的强大特性之一,一般而言,如果不使用动态SQL来拼接SQL语句,是比较痛苦的,比如拼接时要确保不能漏空格,还要注意去掉列表最后一个列名的逗号等,但是利用动态 SQL,就可以彻底摆脱这种痛苦。
一般而言,使用mybatis有两种配置,一种是通过xml文件的方式来配置,另一种是通过注解的方式来配置。
1、xml文件
mybatis的*mapper.xml文件里能够使用动态SQL的标签有4种,分别是:
① if
if标签是Mybatis中使用动态SQL比较频繁的地方,尤其是在where的判断里,比如:
这里的SQL语句就提供了选择情景,如果我们不传入title或者传入的title为空,那么就不会拼接 AND title like #{title}
又或者想加入额外的判断:
结论:if标签里的 test属性,可以插入并解析OGNL表达式
② choose (when, otherwise)
根据官方文档中的说明
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
结论:when标签里的 test属性,可以插入并解析OGNL表达式
③ trim (where, set)
可以看下这个SQL语句,假设如果没有满足匹配的条件,那么最终这条 SQL 会变成这样:
毫无疑问,这会导致查询失败
同样的,如果匹配的只是第二个条件,这条 SQL 会是这样:
这个查询也会失败
所以mybatis提出来了trim方法,如下:
可以看到多了一个where标签,同理还有一个set标签
结论:该情况下,一般没有地方可以供我们插入OGNL表达式
④ foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
结论:该情况下,一般没有地方可以供我们插入OGNL表达式
⑤ bind
bind 标签允许我们在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:
结论:bind标签里的 value属性,可以插入并解析OGNL表达式
2、注解
springboot使我们摆脱了各种xml配置的烦恼,对应的,mybatis也为springboot提供了对应的注解来满足动态SQL的功能,主要有以下注解:
- @Insert
- @Update
- @Delete
- @Select
- @InsertProvider
- @SelectProvider
- @UpdateProvider
- @DeleteProvider
@Insert、@Update、@Delete和@Select这四个注解对应的是数据库增删改查功能,每一个都有一个对应的Provider注解标识
带有Provider注解和不带有Provider注解的区别是,使用Provider需要自己实现查询类,并且使用动态SQL也简单很多。
举个例子,如果@Update注解想要实现动态SQL,那么一定要使用 **
“})
void updateAuthorValues(Author author);
SelectProvider 调用的方法为findTeacherByName,如下:
可以看到,这种方式没有任何标签,但是同样实现了动态SQL
漏洞分析
场景分析
前面说了动态SQL的基础知识,可以看到,主要就一个点,在动态SQL中,可以解析OGNL表达式
那么是不是说,如果我们控制了一个变量,并且该变量可以被解析成OGNL表达式,是不是就能够实现OGNL表达式注入呢?
答案是肯定的。
经过研究,总结出变量可以被解析成OGNL表达式,主要有以下几个地方:
- if标签里的 test属性
此属性一般写死,不可控
- when标签里的 test属性
此属性一般写死,不可控
- bind标签里的 value 属性
bind标签value属性是可以传值的,如:
但经过测试发现,这里进行OGNL表达式解析的时候,是有顺序的
假设令name的值为:${@java.lang.Math@min(4,10)}
我们想要的执行顺序是这样的:
先利用OGNL表达式解析器来获取${@java.lang.Math@min(4,10)}的值,得到值以后,再将其赋给bind标签中的value,即:
但实际上并非如此,mybatis对于bind中value属性的OGNL解析流程是这样的,
首先利用OGNL表达式解析器解析value的值,此时值单纯为name变量,即:
然后得到值,${@java.lang.Math@min(4,10)},然后将其赋给bind标签value属性中的name变量,即:
这也就导致我们无法令传入的变量的值被OGNL表达式解析器来进行解析,也就无法实现OGNL表达式注入
- ${param} 参数中
${param} 和 【bind标签里的 value属性】同理,虽然可以传值, 但是存在解析顺序问题,同样无法实现OGNL表达式注入
比如存在以下select标签 :
传入的name为:${@java.lang.Math@min(4,10)}
那么其解析过程为:
首先利用OGNL解析器解析${}标签里的内容,解析完毕以后得到name的变量,传入SQL中:
- Provider实现类中的拼接到SQL里的变量
在注解部分里,曾经提到:
可以看到,返回值实际上就是一个SQL语句
没错,Provider其实就是要返回一个SQL字符串,只不过用了一些关键字做格式化而已,其实不使用也可以,比如:
甚至可以使用String字符串拼接SQL语句:
亦或者使用String.format来处理:
有时候复杂的语句还可以使用StringBuilder或者StringBuffer拼接,如:
这样形成的SQL语句,实际上就是相当于生成了一个XML文件:
- 作者:qetx
- 链接:http://qetx.top/article/4e655ac0-5da5-447c-adb9-ccfcbfcfdcb9
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。








