SQL Injection: MyBatis Mapper
Abstract:
在xxx.xml的第32行,可能使用不可信赖的数据源输入构建SQL查询。通过这种调用,攻击者能够修改语句的含义或者执行任意SQL命令。
Explanation:
SQL injection错误在以下情况下发生:
1. 数据从一个不可信赖的数据源进入程序。
2. 数据用于动态地构造一个SQL查询。
使用MyBatis Mapper XML文件可在SQL语句中指定动态参数,而这些文件通常使用#字符来定义,如下所示:
<select id="getItems" parameterType="domain.company.MyParamClass" resultType="MyResultMap"> SELECT * FROM items WHERE owner = #{userName} </select>
变量名称周围带有括号的#字符表示MyBatis将使用userName变量创建参数化查询。但是,MyBatis还允许使用$字符将变量直接连接到SQL指令,使其易受SQL injection攻击。
例1:以上代码动态地构造并执行了一个SQL查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。
<select id="getItems" parameterType="domain.company.MyParamClass" resultType="MyResultMap"> SELECT * FROM items WHERE owner = #{userName} AND itemname = ${itemName} </select>
但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在itemName不包含单引号字符时,才会正确执行这一查询。如果一个用户名为wiley的攻击者为itemName输入字符串" name 'OR' 'a'='a ", 那么查询就会变成:
SELECT * FROM items WHERE owner = "wiley" AND itemname = 'name' OR 'a'='a';
附加条件 OR 'a'='a'会使WHERE从句的计算结果始终为true,因此该查询在逻辑上将等同于一个更为简化的查询:
SELECT * FROM items;
这种查询简化使得攻击者能够避开查询只返回通过身份验证的用户所有的条目这一要求。现在查询会返回所有存储在items表中的条目,不论它们的指定所有者是谁。
示例2:此示例说明了将不同的恶意值传递给Example1.中构造和执行的查询所带来的影响。如果一个用户名为wiley的攻击者为itemName输入字符串“ name';DELETE FROM items;--”,则该查询就会变为以下两个查询:
SELECT * FROM items WHERE owner = 'wiley' AND itemname = 'name'; DELETE FROM items; --'
众多数据库服务器,其中包括Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的SQL指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如Oracle和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。注意末尾的一对连字符(--);这在大多数数据库服务器上都表示该语句剩余部分将视为注释,不会执行。在这种情况下,可通过注释字符删除修改后的查询遗留的末尾单引号。而在不允许通过这种方式使用注释的数据库上,攻击者通常扔可使用类似于Example1中所用的技巧进行攻击。如果攻击者输入字符串" name');DELETE FORM items;SELECT * FROM items WHERE 'a'='a ",将创建一下三个有效语句:
SELECT * FROM items;
WHERE owner='wiley'
AND itemname='name';
DELETE FROM items;
SELECT * FROM items WHERE 'a'='a';
避免SQL injection攻击的传统方法之一是,作为一个输入验证问题来处理,只接受列在安全值允许列表中的字符,或者识别并避免列在潜在恶意值列表(拒绝列表)中的字符。检验允许列表是一种非常有效的方法,它可以强制执行严格的输入验证规则,但是参数化的SQL语句所需的维护工作更少,而且能提供更好的安全保障。而对于通常采用的执行拒绝列表方式,由于总是存在一些小漏洞,所以并不能有效地防止SQL injection攻击。例如,攻击者可以:
— 把没有被黑名单引用的值作为目标
— 寻找方法以绕过某些需要转义的元字符
— 使用存储过程隐藏注入的元字符
手动去除SQL查询中的元字符有一定的帮助,但是并不能确保应用程序免受SQL injection攻击。