SQL注入(SQL Injection)

一、规则中文名称:
SQL注入
二、规则英文名称:
SQL Injection
三、规则子类中文名称:
无
四、规则子类英文名称:
无
五、规则概述:
采用来自一个不可信赖的数据源的数据动态构造一个 SQL 查询。
六、规则详情:
采用来自一个不可信赖的数据源的数据动态构造一个 SQL 查询,会导致sql注入漏洞。注意:动态构造一个SQL查询并不一定就能带来sql注入,只有在动态构造SQL语句时的参数来自不可信赖的数据源(如来自外部接口调用、来自配置文件等)时才会导致sql注入漏洞。
例1:以下代码动态地构造并执行了一个 SQL 查询,该查询搜索与指定授权用户和指定项目称相匹配的项。
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String query = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
ResultSet rs = stmt.execute(query);
这一代码所执行的查询遵循如下方式:
SELECT * FROM items WHERE owner = AND itemname = ;
但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在 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 表中的条目。
七、整改建议:
1、参数化 SQL语句可以防止直接窜改上下文,避免 SQL injection 攻击。
参数化 SQL 指令是用常规的 SQL 字符串构造,当需要加入用户输入的数据时,使用捆绑参数,这些捆绑参数是一些占位符,用来存放随后插入的数据。换言之,捆绑参数可以使程序员清楚地分辨数据库中的数据,即其中有哪些输入可以看作命令的一部分,哪些输入可以看作数据。这样,当程序准备执行某个指令时,它可以详细地告知数据库,每一个捆绑参数所使用的运行时的值,而不会被解析成对该命令的修改。可以将例 1 改写成使用参数化 SQL 指令(替代用户输入连续的字符串),如下所示:
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String query = "SELECT * FROM items WHERE itemname=? AND owner=?";
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, itemName);
stmt.setString(2, userName);
ResultSet results = stmt.execute();
2、使用白名单来过滤来自不可信的参数。
当必须要根据用户输入来改变sql命令结构时,可以使用间接的方法来防止 SQL injection 攻击:创建一个合法的字符串集合,使其对应于可能要加入到 SQL 指令中的不同元素,在构造一个sql指令时,可根据用户输入从应用程序控制的值集合中选择字符串。
另外,可以作为一个输入合法性检查的问题来处理,只接受列在白名单中的字符,或者识别并避免那些列在黑名单中的恶意数据。白名单方法是一种非常有效方法,它可以强制执行严格的输入检查规则。而对于黑名单方式,由于总是存在一些小漏洞,并不能有效地防止 SQL injection 威胁。
八、表现形式:
(一)使用 Hibernate 来执行通过不可信来源的输入构建的动态 SQL 语句会使得攻击者能够篡改语句的含义或者执行任意 SQL 命令。 示例 :以下代码动态地构造并执行一个 HQL 查询,用于搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。...
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String query = "FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
List items = sess.createQuery(query).list();
(二)使用 Java 数据对象 (JDO) 执行通过不可信来源的输入构建的动态 SQL 或 JDOQL 指令,会让攻击者有机会篡改指令的含义或者执行任意 SQL 命令。 例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。...
String userName = ctx.getAuthenticatedUserName();
String itemName = request.getParameter("itemName");
String sql = "SELECT * FROM items WHERE owner = '"
+ userName + "' AND itemname = '"
+ itemName + "'";
Query query = pm.newQuery(Query.SQL, sql);
query.setClass(Person.class);
List people = (List)query.execute();
...
(三)MyBaits配置导致SQL注入
MyBatis允许使用 $ 字符将变量直接连接到 SQL 指令,使其易受 SQL injection 攻击。
例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。
SELECT *
FROM items
WHERE owner = #{userName}
AND itemname = ${itemName}
但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在 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 表中的条目,不论它们的所有者是谁。 (四) iBaits数据配置使用 iBatis Data Map 可以指定 SQL 指令中的动态参数,iBatis 允许使用 $ 字符将变量直接连接到 SQL 指令,使其易受 SQL injection 攻击。
例 1:以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。
SELECT * FROM items WHERE owner = #userName# AND itemname = '$itemName$'
但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在 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 表中的条目,不论它们的所有者是谁。