您的位置:首页 > 数据库

【web漏洞百例】3.Sql注入、不安全的随机数

2017-03-28 22:19 375 查看
一、Sql注入

描述:


通过不可信来源的输入构建动态SQL指令,攻击者就能够修改指令的含义或者执行任意SQL命令。

举例:

Sql注入错误会在以下情况发生

1.数据从一个不可信赖的数据源进入程序。

2.数据用于动态地构造一个SQL语句

使用Java的MyBatis/iBatis框架可以指定SQL指令中的动态参数,通常使用#字符来定义它们,如:
<select id="getItems" parameterClass="Myclass" returnClass="items">
SELECT * FROM items WHERE owner=#userName#
</select>


变量名称周围的#字符表示iBatis将使用userName变量创建参数化查询,但是iBatis还允许使用$

将变量直接连接到SQL指令,使其易受SQL注入攻击。

提示:

MyBatis/Ibatis中#和$的区别

1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id".

 

2. $将传入的数据直接显示生成在sql中。如:order by $user_id$,如果传入的值是111,那么解析成sql时的值为order by user_id,  如果传入的值是id,则解析成的sql为order by id.

例1:一下代码动态的构造并执行了一个SQL查询,该查询可以搜索与指定名称相匹配的项。

该查询仅会显示条目所有者与被授予权限的当前用户一直的条目。
<select id="getItems" parameterClass="Myclass" returnClass="items">
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:这个例子指出了不同的恶意数值传递在例1中构造和执行的查询所带来的各种影响。如果一个

用户名为wiley的攻击者为itemName输入字符"name';DELETE FROM items;--",那么构造成的查询语

句将会变成两个:
SELECT * FROM items
WHERE owner='wiley'
AND itemName='name'
DELETE FROM items;--'


众多数据库服务器,其中包括Microsoft(R) SQL Sever 2000,都可以一次性执行多条用分号分隔的

SQL指令。对于那些不允许使用分号分隔的批量SQL指令的数据库服务器,比如Oracle和其它数据库

服务器,攻击者输入的字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者会

通过执行多条SQL而在数据库上执行任意SQL指令。

注意成对的连字符(--);这在大多数数据库服务器上都表示下面的语句将作为注释使用,而不能加以

执行。在这种情况下,注释字符的作用就是去删除修改后的查询指令中遗留的最后一个单引号。而

在那些不允许这样加注注释的数据库中,通常攻击这可以如例1那样来攻击。

避免SQL注入攻击的传统方法之一是,把它作为一个输入合法检查的问题来处理,只接受列在白名单

中的字符,或者识别并避免那些列在黑名单中的恶意数据。白名单方法是一种非常有效的方法,它可

以强制执行严格的输入校验检查规则,而参数化的SQL指令所需维护更少,而且能够提供更好的安全

保障。而对于通常采用的列黑名单的方式,由于总是存在一些小漏洞,所以并不能有效地防止SQL注入

的威胁。例如,攻击者可以:

--把没有被黑名单所引用的值作为目标

--寻找方法以绕过对某一列转义序列元字符的需要

--通过存储过程来隐藏注入的元字符

手动去除SQL查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受SQL注入攻击。

防范SQL注入攻击的另外一种方式是使用存储过程。虽然存储过程可以阻止某些类型的SQL注入攻击,

但是对于绝大多数攻击仍然无能为力。存储过程有助于避免SQl注入的常用方式是限制可作为参数传

入的指令类型。但是,有许多方法都可以绕开这一限制,许多危险的表达式仍可以传入存储过程。

再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用抵御SQL注入的

攻击。

解决方案:

造成SQL注入攻击的根本原因是在于攻击者可以改变SQL查询的上下文,使程序员原本要作为数据解析

的数值,被篡改为命令了。当构造一个SQL查询时,程序员应当清楚,哪些输入的数据将会成为命令

的一部分,而哪些仅仅是作为数据。

参数化的SQL指令可以防止直接篡改上下文,避免几乎所有的SQL注入攻击。参数化SQL指令是用常规

的SQL字符串构造的,但是当需要加入用户输入的数据时,它们就需要使用捆绑参数,这些捆绑参数

是一些占位符,用来存放随后插入的数据。换言之,捆绑参数可以使清楚地分辨数据库中的数据,

即其中有哪些输入可以看作命令的一部分,哪些输入可以看做数据。这样,当程序准备执行某个命令

时,它可以详细的告知数据库,每一个捆绑参数使用的运行时的值,而不会被解析成对该命令的修改。

例3:可以将例1重写未使用SQL参数化SQL指令(替代用户输入连续的字符串),如下所示:
<select id="getItems" parameterClass="Myclass" returnClass="items">
SELECT * FROM items WHERE owner=#userName# AND itemName=#itemName#
</select>


更加复杂的情况常常出现在报表生成代码中,因为这时需要通过用户输入来改变SQL指令的命令结构,

比如在WHERE条件子句中加入动态的约束条件。不要因为这一需求,就无条件地接受连续的用户输入,

从而创建查询语句字符串。当必须要根据用户输入来改变命令结构时,可以使用间接的方式来防止SQL

注入的攻击:创建一个合法的字符串集合,使其对应于可能要加入在SQL指令中的不同元素。在构造

一个SQL指令时,可以使用来自用户的输入,以便从应用程序控制的值集合中进行选择。

注:我有一篇专门探讨Sql注入以及解决方案的文章,链接:http://blog.csdn.net/acmman/article/details/48862841

二、不安全的随机数

描述:


标准的伪随机数值生成器不能抵挡各种加密攻击

举例:

在对安全性要求高的环境中,使用一个能产生可能预测数值的函数作为随机数据源,会产生“不安全

随机数”的错误。

电脑是一中具有确定性的机器,因此不可能产生真正的随机性。伪随机数生成器(PRNG)近似于随机

算法,始于一个能计算后续数值的种子。

PRNG包括两种类型:统计学的PRNG和密码学的PRNG。统计学的PRNG可提供有用的统计资料,但其输出

结果很容易预测,因此数据流容易复制。若安全性取决于生成数值的不可预测性,则此类型不适用。

密码学的PRNG通过可产生较难预测的输入结果来应对这一问题。为了使加密数值更为安全,必须使攻击

者根本无法、或极不可能将它与真实的随机数加以区分。通常情况下,如果未声明PRNG算法带有加密
保护,那么它有可能就是一个统计学的PRNG,不应在对安全性要求较高的环境中使用,其中随着它的使用

可能会导致严重的漏洞(如易于猜测的密码、可预测的加密密钥、会话劫持攻击和DNS欺骗)。

示例:下面的代码可利用统计学的PRNG为购买产品后仍在有效期内的收据创建一个URL。
String GenerateReceiptURL(String baseUrl){
Random ranGen = new Random();
ranGen.setSeed((new Date()).getTime());
return (baseUrl+ranGen.nextInt(400000000)+".html");
}


这段代码使用Random.nextInt()函数作为它所生成的收据页面生成独特的标识符。因为

Random.nextInt()是一个统计学的PRNG,攻击者很容易猜到由它所生成的字符串。尽管

收据系统的底层设计也存在错误,但如果使用了一个不生成可预测收据标识符的随机数

生成器(如密码学的PRNG),会更安全一点。

解决方案:

当不可预测性至关重要时,如大多数对安全性要求较高的环境都采用随机性,这时可以使用密码学

的PRNG。不管选择了哪一种PRNG,都要始终使用带有充足熵的数值作为该算法的种子。(诸如当前

时间之类的数值只提供很小的熵,因此不应该使用。)

Java语言在java.security.SecureRandom中提供了一个加密PRNG。就像java.security中其它以算法

为基础的类那样,SecureRandom提供了与某个特定算法集合相关的包,该包可以独立实现。当使用

SecureRandom.getInstance()请求一个SecureRandom实例时,您可以申请实现某个特定的算法。如果

算法可行,那么您可以将它作为SecureRandom的对象使用。如果算法不可行,或者您没有为算法明确

特定的实现方法,那么会由系统为您选择SecureRandom的实现方法。

Sun在名为SHA1PRNG的Java版本中提供了一种单独实现SecureRandom的方式,Sun将其描述为:

“SHA-1”可以计算一个真实的随机种子参数的散列值,同时,该种子参数带有一个64比特的计算器,

会在每一次操作后加1。在160比特的SHA-1的输出中,只能使用64比特的输出”

然而,文档中有关sun的SHA1PRNG算法实现细节相关记录很少,人们无法了解算法实现中使用的熵的
来源,因此也不清楚输出中到底存在多少真实的随机数值。尽管有关sun的实现方法网络上有各种各样的猜测,

但是有一点毋庸置疑,即算法具有很强的加密性,可以在对安全性极为隐秘的各种内容中安全地使用。

转载请注明出处:http://blog.csdn.net/acmman/article/details/67660622
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: