腾冲

Web安全之SQL注入攻击技巧与防范

from: http://www.plhwin.com/2014/06/13/web-security-sql/Web安全简史在Web1.0时代,人们更多是关注服务器端动态脚本语言的安全问题,比如将一个可执行脚本(俗称Webshell)通过脚本语言的漏洞上传到服务器上,从而获得服务器权限。在Web发展初期,随着动态脚本语言的发展和普及,以及早期工程师对安全问题认知不足导致很多”安全血案”的发生,至今仍然遗留下许多历史问题,比如PHP语言至今仍然无法从语言本身杜绝「文件包含漏洞」(参见这里),只能依靠工程师良好的代码规范和安全意识。伴随着Web2.0、社交网络、微博等一系列新型互联网产品的兴起,基于Web环境的互联网应用越来越广泛,Web攻击的手段也越来越多样,Web安全史上的一个重要里程碑是大约1999年发现的SQL注入攻击,之后的XSS,CSRF等攻击手段愈发强大,Web攻击的思路也从服务端转向了客户端,转向了浏览器和用户。在安全领域,一般用帽子的颜色来比喻黑客的善与恶,白帽子是指那些工作在反黑客领域的技术专家,这个群体是”善”的的象征;而黑帽子则是指那些利用黑客技术造成破坏甚至谋取私利造成犯罪的群体,他们是”恶”的代表。“白帽子”和”黑帽子”是两个完全对立的群体。对于黑帽子而言,他们只要找到系统的一个切入点就可以达到入侵破坏的目的,而白帽子必须将自己系统所有可能被突破的地方都设防,以保证系统的安全运行。这看起来好像是不公平的,但是安全世界里的规则就是这样,可能我们的网站1000处都布防的很好,考虑的很周到,但是只要有一个地方疏忽了,攻击者就会利用这个点进行突破,让我们另外的1000处努力白费。常见攻击方式一般说来,在Web安全领域,常见的攻击方式大概有以下几种:1、SQL注入攻击2、跨站脚本攻击 - XSS3、跨站伪造请求攻击 - CSRF4、文件上传漏洞攻击5、分布式拒绝服务攻击 - DDOS说个题外话,本来这篇文章一开始的标题叫做 「Web安全之常见攻击方法与防范」,我原本想把上面的这5种方法都全部写在一篇文章里,可是刚写完第一个SQL注入攻击的时候,就发现文章篇幅已经不短了,又很难再进行大幅度的精简,所以索性把Web安全分成一个系列,分多篇文章来呈现给大家,下面你看到的就是第一篇「Web安全之SQL注入攻击的技巧与防范」。SQL注入常见攻击技巧SQL注入攻击是Web安全史上的一个重要里程碑,它从1999年首次进入人们的视线,至今已经有十几年的历史了,虽然我们现在已经有了很全面的防范对策,但是它的威力仍然不容小觑,SQL注入攻击至今仍然是Web安全领域中的一个重要组成部分。以PHP+MySQL为例,让我们以一个Web网站中最基本的用户系统来做实例演示,看看SQL注入究竟是怎么发生的。1、创建一个名为demo的数据库:CREATE DATABASE `demo` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;2、创建一个名为user的数据表,并插入1条演示数据:CREATE TABLE `demo`.`user` (`uid` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '用户uid',`username` VARCHAR( 20 ) NOT NULL COMMENT '用户名',`password` VARCHAR( 32 ) NOT NULL COMMENT '用户密码') ENGINE = INNODB;INSERT INTO `demo`.`user` (`uid`, `username`, `password`) VALUES ('1', 'plhwin', MD5('123456'));实例一通过传入username参数,在页面打印出这个会员的详细信息,编写 userinfo.php 程序代码:<?phpheader('Content-type:text/html; charset=UTF-8');$username = isset($_GET['username']) ? $_GET['username'] : '';$userinfo = array();if($username){ //使用mysqli驱动连接demo数据库 $mysqli = new mysqli("localhost", "root", "root", 'demo'); $sql = "SELECT uid,username FROM user WHERE username='{$username}'"; //mysqli multi_query 支持执行多条MySQL语句 $query = $mysqli->multi_query($sql); if($query){ do { $result = $mysqli->store_result(); while($row = $result->fetch_assoc()){ $userinfo[] = $row; } if(!$mysqli->more_results()){ break; } } while ($mysqli->next_result()); }}echo '<pre>',print_r($userinfo, 1),'</pre>';上面这个程序要实现的功能是根据浏览器传入的用户名参数,在页面上打印出这个用户的详细信息,程序写的这么复杂是因为我采用了mysqli的驱动,以便能使用到 `multi_query` 方法来支持同时执行多条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] =&gt; Array ( [Tables_in_demo] =&gt; user ))你能清晰的看到,除了会员的信息,数据库表的名字`user`也被打印在了页面上,如果作恶的黑客此时将参数换成 `plhwin';DROP TABLE user-- hack`,那将产生灾难性的严重结果,当你在浏览器中执行`http://localhost/test/userinfo.php?username=plhwin';DROP TABLE user-- hack` 这个URL后,你会发现整个 `user` 数据表都消失不见了。通过上面的例子,大家已经认识到SQL注入攻击的危害性,但是仍然会有人心存疑问,MySQL默认驱动的mysql_query方法现在已经不支持多条语句同时执行了,大部分开发者怎么可能像上面的演示程序那样又麻烦又不安全。是的,在PHP程序中,MySQL是不允许在一个mysql_query中使用分号执行多SQL语句的,这使得很多开发者都认为MySQL本身就不允许多语句执行了,但实际上MySQL早在4.1版本就允许多语句执行,通过PHP的源代码,我们发现其实只是PHP语言自身限制了这种用法,具体情况大家可以看看这篇文章「PHP+MySQL多语句执行」。实例二如果系统不允许同时执行多条SQL语句,那么SQL注入攻击是不是就不再这么可怕呢?答案是否定的,我们仍然以上面的user数据表,用Web网站中常用的会员登录系统来做另外一个场景实例,编写程序login.php,代码如下:<?phpif($_POST){…