sql注入攻击

转载::

常见攻击方式

一般说来,在Web安全领域,常见的攻击方式大概有以下几种:
1、SQL注入攻击
2、跨站脚本攻击 - XSS
3、跨站伪造请求攻击 - CSRF
4、文件上传漏洞攻击

5、分布式拒绝服务攻击 - DDOS


SQL注入产生的原因,和栈溢出、XSS等很多其他的攻击方法类似,就是未经检查或者未经充分检查用户输入数据意外变成了代码被执行也就是,SQL注入是用户输入的数据,在拼接SQL语句的过程中,超越了数据本身,成为了SQL语句查询逻辑的一部分,然后这样被拼接出来的SQL语句被数据库执行,产生了开发者预期之外的动作。所以从根本上防止上述类型攻击的手段,还是避免数据变成代码被执行,时刻分清代码和数据的界限。而具体到SQL注入来说,被执行的恶意代码是通过数据库的SQL解释引擎编译得到的,所以只要避免用户输入的数据被数据库系统编译就可以了。

现在的数据库系统都提供SQL语句的预编译(prepare)和查询参数绑定功能,在SQL语句中放置占位符'?',然后将带有占位符的SQL语句传给数据库编译,执行的时候才将用户输入的数据作为执行的参数传给用户。这样的操作不仅使得SQL语句在书写的时候不再需要拼接,看起来也更直接,而且用户输入的数据也没有机会被送到数据库的SQL解释器被编译执行,也不会越权变成代码。

假设我们可以通过 http://localhost/test/userinfo.php?username=plhwin 这个URL来访问到具体某个会员的详情,正常情况下,如果浏览器里传入的username是合法的,那么SQL语句会执行:

SELECT uid,username FROM user WHERE username='plhwin'

但是,如果用户在浏览器里把传入的username参数变为 plhwin';SHOW TABLES-- hack,也就是当URL变为 http://localhost/test/userinfo.php?username=plhwin';SHOW TABLES-- hack 的时候,此时我们程序实际执行的SQL语句变成了:

SELECT uid,username FROM user WHERE username='plhwin';SHOW TABLES-- hack'

注意:在MySQL中,最后连续的两个减号表示忽略此SQL减号后面的语句(然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用,这样语句永远都能正确执行,用户轻易骗过系统,获取合法身份。),我本机的MySQL版本号为5.6.12,目前几乎所有SQL注入实例都是直接采用两个减号结尾,但是实际测试,这个版本号的MySQL要求两个减号后面必须要有空格才能正常注入,而浏览器是会自动删除掉URL尾部空格的,所以我们的注入会在两个减号后面统一添加任意一个字符或单词,本篇文章的SQL注入实例统一以 -- hack 结尾。

经过上面的SQL注入后,原本想要执行查询会员详情的SQL语句,此时还额外执行了 SHOW TABLES; 语句,这显然不是开发者的本意,此时可以在浏览器里看到页面的输出:

Array
(
    [0] => Array
        (
            [uid] => 1
            [username] => plhwin
        )
    [1] => Array
        (
            [Tables_in_demo] => user
        )
)

你能清晰的看到,除了会员的信息,数据库表的名字user也被打印在了页面上,如果作恶的黑客此时将参数换成 plhwin';DROP TABLE user-- hack,那将产生灾难性的严重结果,当你在浏览器中执行 http://localhost/test/userinfo.php?username=plhwin';DROP TABLE user-- hack 这个URL后,你会发现整个 user 数据表都消失不见了。

此时如果输入正确的用户名 plhwin 和密码 123456,执行的SQL语句为:

SELECT uid,username FROM user WHERE username='plhwin' AND password='e10adc3949ba59abbe56e057f20f883e'

上面语句没有任何问题,可以看到页面打印出了登录成功后的会员信息,但如果有捣蛋鬼输入的用户名为 plhwin' AND 1=1-- hack,密码随意输入,比如 aaaaaa,那么拼接之后的SQL查询语句就变成了如下内容:

SELECT uid,username FROM user WHERE username='plhwin' AND 1=1-- hack' AND password='0b4e7a0e5fe84ad35fb5f95b9ceeac79'

执行上面的SQL语句,因为 1=1 是永远成立的条件,这意味着黑客只需要知道别人的会员名,无需知道密码就能顺利登录到系统。

如何确定SQL注入漏洞

通过以上的实例,我们仍然还会有疑问:黑客并不知道我们程序代码的逻辑和SQL语句的写法,他是如何确定一个网站是否存在SQL注入漏洞呢?一般说来有以下2种途径:

1、错误提示

如果目标Web网站开启了错误显示,攻击者就可以通过反复调整发送的参数、查看页面打印的错误信息,推测出Web网站使用的数据库和开发语言等重要信息。

2、盲注

除非运维人员疏忽,否则大部分的Web运营网站应该都关闭了错误提示信息,此时攻击者一般会采用盲注的技巧来进行反复的尝试判断。 仍然以上面的数据表user为例,我们之前的查看会员详情页面的url地址为 userinfo.php?username=plhwin,此时黑客分别访问 userinfo.php?username=plhwin' AND 1=1-- hack userinfo.php?username=plhwin' AND 1=2-- hack,如果前者访问能返回正常的信息而后者不能,就基本可以判断此网站存在SQL注入漏洞,因为后者的 1=2 这个表达式永远不成立,所以即使username传入了正确的参数也无法通过,由此可以推断这个页面存在SQL注入漏洞,并且可以通过username参数进行注入。

如何防御SQL注入

1、对于服务器配置层面的防范,应该保证生产环境的Webserver是关闭错误信息的,比如PHP在生产环境的配置文件php.ini中的display_errors应该设置为Off,这样就关闭了错误提示,下面我们更多的从编码的角度来看看如何防范SQL注入。

但凡有SQL注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或URL传递的参数,并且这个变量或参数是组成SQL语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,这是安全领域里的「外部数据不可信任」的原则,纵观Web安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到的,就是从变量的检测、过滤、验证下手,确保变量是开发者所预想的。
1、检查变量数据类型和格式

如果你的SQL语句是类似where id={$id}这种形式,数据库里所有的id都是数字,那么就应该在SQL被执行前,检查确保变量id是int类型;如果是接受邮箱,那就应该检查并严格确保变量一定是邮箱的格式,其他的类型比如日期、时间等也是一个道理。总结起来:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。

2、过滤特殊符号

对于无法确定固定格式的变量,一定要进行特殊符号过滤或转义处理。以PHP为例,通常是采用addslashes函数,它会在指定的预定义字符前添加反斜杠转义,这些预定义的字符是:单引号 (') 双引号 (") 反斜杠 (\) NULL。

来看2条SQL语句:

$uid = isset($_GET['uid']) ? $_GET['uid'] : 0;
$uid = addslashes(uid);
$sql = "SELECT uid,username FROM user WHERE uid='{$uid}'";//此处多增加引号

以及

$uid = isset($_GET['uid']) ? $_GET['uid'] : 0;
$uid = addslashes(uid);
$sql = "SELECT uid,username FROM user WHERE uid={$uid}";
上面两个查询语句都经过了php的addslashes函数过滤转义,但在安全性上却大不相同,在MySQL中,对于 int类型字段 的条件查询,上面个语句的查询效果完全一样,由于第一句SQL的变量被单引号包含起来,SQL注入的时候,黑客面临的首要问题是必须要先闭合前面的单引号,这样才能使后面的语句作为SQL执行,并且还要注释掉原SQL语句中的后面的单引号,这样才可以成功注入,由于代码里使用了addslashes函数,黑客的攻击会无从下手,但第二句没有用引号包含变量,那黑客也不用考虑去闭合、注释,所以即便同样采用addslashes转义,也还是存在SQL攻击漏洞。
默认情况下,PHP 指令 magic_quotes_gpc 为 on,系统会对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes() 。不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes() ,因为这样会导致双层转义。

对于PHP程序+MySQL构架的程序,在动态的SQL语句中,使用单引号把变量包含起来配合addslashes函数是应对SQL注入攻击的有效手段,但这做的还不够,像上面的2条SQL语句,根据「检查数据类型」的原则, uid都应该经过intval函数格式为int型 ,这样不仅能有效避免第二条语句的SQL注入漏洞,还能使得程序看起来更自然,尤其是在NoSQL(如MongoDB)中,变量类型一定要与字段类型相匹配才可以。

从上面可以看出,第二个SQL语句是有漏洞的,不过由于使用了addslashes函数,你会发现黑客的攻击语句也存在不能使用特殊符号的条件限制,类似where username='plhwin'这样的攻击语句是没法执行的,但是黑客可以将字符串转为16进制编码数据或使用char函数进行转化,同样能达到相同的目的,如果对这部分内容感兴趣,可以(http://cbb.sjtu.edu.cn/course/database/lab8.htm)这里查看。而且由于SQL保留关键字,如「HAVING」、「ORDER BY」的存在,即使是基于黑白名单的过滤方法仍然会有或多或少问题,那么是否还有其他方法来防御SQL注入呢?

3、绑定变量,使用预编译语句

实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,在SQL语句中,变量用问号?表示,黑客即使本事再大,也无法改变SQL语句的结构,像上面例子中,username变量传递的 plhwin' AND 1=1-- hack 参数,也只会当作username字符串来解释查询,从根本上杜绝了SQL注入攻击的发生。

数据库信息加密安全

在Web开发中,传统的加解密大致可以分为三种:

1、对称加密:

即加密方和解密方都使用相同的加密算法和密钥,这种方案的密钥的保存非常关键,因为算法是公开的,而密钥是保密的,一旦密匙泄露,黑客仍然可以轻易解密。常见的对称加密算法有:AES、DES等。

2、非对称加密:

即使用不同的密钥来进行加解密,密钥被分为公钥和私钥,用私钥加密的数据必须使用公钥来解密,同样用公钥加密的数据必须用对应的私钥来解密,常见的非对称加密算法有:RSA等。

3、不可逆加密:

利用哈希算法使数据加密之后无法解密回原数据,这样的哈希算法常用的有:md5、SHA-1等。

在我们上面登录系统的示例代码中,$md5password = md5($password); 从这句代码可以看到采用了md5的不可逆加密算法来存储密码,这也是多年来业界常用的密码加密算法,但是这仍然不安全。为什么呢?

这是因为md5加密有一个特点:同样的字符串经过md5哈希计算之后生成的加密字符串也是相同的,由于业界采用这种加密的方式由来已久,黑客们也准备了自己强大的md5彩虹表来逆向匹配加密前的字符串,这种用于逆向反推MD5加密的彩虹表在互联网上随处可见,在Google里使用md5 解密作为关键词搜索,一下就能找到md5在线破解网站,把我们插入用户数据时候的MD5加密字符串e10adc3949ba59abbe56e057f20f883e填入进去,瞬间就能得到加密前的密码:123456。当然也并不是每一个都能成功,但可以肯定的是,这个彩虹表会越来越完善。

所以,我们有迫切的需求采用更好的方法对密码数据进行不可逆加密,通常的做法是为每个用户确定不同的密码加盐(salt)后,再混合用户的真实密码进行md5加密,如以下代码:

小结

1、不要随意开启生产环境中Webserver的错误显示。
2、
永远不要信任来自用户端的变量输入,有固定格式的变量一定要严格检查对应的格式,没有固定格式的变量需要对引号等特殊字符进行必要的过滤转义
3、
使用预编译绑定变量的SQL语句
4、做好数据库帐号权限管理。
5、严格加密处理用户的机密信息。

根治方法:

1、尽量不要在应用层拼接SQL字符串

2、必须拼接字符串的时候对参数做好过滤

3、认真检查是否存在例外

对于MySQL等开源数据库,不允许不带where条件(或者where条件恒为true)的delete和update,即safe update模式,通过定制patch的方式限制用户修改的数据行数,在sql中增加hint传递期望修改数据的数目,如果真实更新数据与目标不符,回滚操作

备份很重要,即使发生这类悲剧,通过上一次备份重建数据库和重放最近更新来挽救

时隔很久看自己发的都感觉搞笑,目前我是这样做的:

把每一个参数内部的'都换成''(这样你存进去的还是一个',否则会被截断造成注入)

然后把参数两端加个',这样不管传进来的是什么,都会被转化为一个正常的参数传入。

如果你是使用PHP的话,只要使用PDO,就不会存在注入问题。
























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值