Web安全的SQL注入攻击技巧及预防

Web安全简史

以Web1.0时期,人们还多是关心服务器端动态脚本语言的平安问题,比如用一个而实施脚本(俗称Webshell)通过脚本语言的狐狸尾巴上传到服务器上,从而赢得服务器权限。在Web发展前期,随着动态脚本语言的上进和普及,以及早期工程师对安康问题认知不足造成多”安全血案”的产生,至今还遗留下许多史问题,比如PHP语言至今还是无法从语言本身杜绝「文件包含漏洞」(参见这里),只能借助工程师可以的代码规范及安意识。

陪同在Web2.0、社交网络、微博等同样系列新型互联网产品的兴起,基于Web环境之互联网采用越来越广阔,Web攻击的手腕为愈多样,Web安全史上之一个重中之重里程碑是大体1999年察觉的SQL注入攻击,之后的XSS,CSRF等攻击手段更为强大,Web攻击的思绪为起服务端转向了客户端,转向了浏览器和用户。

在安康世界,一般用帽子的颜色来比喻黑客的轻与恶,白帽子是指那些工作在倒黑客领域的艺专家,这个部落是”善”的底表示;而黑帽子则是凭那些运用黑客技术造成损坏还谋取私利造成违法之群体,他们是”恶”的意味。

“白帽子”和”黑帽子”是零星个全对立的部落。对于黑帽子而言,他们要找到网的一个切入点就可达标入侵破坏的目的,而白帽子必须用自己系统所有或受突破的地方还设防,以保证系统的平安运行。

立即看起好像是勿公道的,但是安全世界里的条条框框就是是这般,可能咱们的网站1000处于还布防的不胜好,考虑的百般周全,但是倘若来一个地方疏忽了,攻击者就见面动用这点展开突破,让咱另外的1000远在努力白费。

科普攻击方式

日常,在Web安全领域,常见的攻击方式大概有以下几种植:
1、SQL注入攻击
2、跨站脚论攻击 – XSS
3、跨站伪造请求攻击 – CSRF
4、文件及污染漏洞攻击
5、分布式拒绝服务攻击 – DDOS

说只书写外话,本来就首文章一开始之题叫做
「Web安全之时表现攻击方法和防范」,我原来想将地方的即时5栽艺术还通写在一如既往篇稿子里,可是刚写了第一单SQL注入攻击的时光,就意识文章篇幅就不缺乏了,又生麻烦再次拓展大幅度的简要,所以索性把Web安全分成一个多元,分多首文章来呈现给大家,下面你看看的就是是首先首「Web安全之SQL注入攻击的技艺及防范」。

SQL注入常见攻击技巧

SQL注入攻击是Web安全史上之一个要里程碑,它由1999年首不善进人们的视线,至今已产生十几年的历史了,虽然我们今天早就生矣大周全的预防对策,但是它的威力还拒绝小视,SQL注入攻击至今依然是Web安全领域面临之一个重中之重部分。

盖PHP+MySQL为例,让我们因为一个Web网站中尽中心的用户系统来开实例演示,看看SQL注入究竟是怎来的。

1、创建一个叫作吧demo的数据库:
<pre>
CREATE DATABASE demo DEFAULT CHARACTER SET utf8 COLLATE
utf8_general_ci;
</pre>

2、创建一个名吧user的数据表,并插入1长长的演示数据:
<pre>
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’));
</pre>

实例一

由此传播username参数,在页面打印出这个会员的详细信息,编写
userinfo.php 程序代码:

<pre>
<?php
header(‘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>’;
</pre>

地方这序一旦贯彻之功效是基于浏览器传入的用户称参数,在页面及打印出之用户的详细信息,程序写的这样复杂是为自以了mysqli的叫,以便能利用及
multi_query
方法来支撑以推行多长长的SQL语句,这样会重复好之证明SQL注入攻击的危害性。

比方我们好透过 http://localhost/test/userinfo.php?username=plhwin
这个URL来拜访到现实有会员的详情,正常状况下,如果浏览器里传开的username是官方的,那么SQL语句会履:

<pre>
SELECT uid,username FROM user WHERE username=’plhwin’
</pre>

不过,如果用户在浏览器里将传播的username参数变为
plhwin';SHOW TABLES-- hack,也就是当URL变为
http://localhost/test/userinfo.php?username=plhwin';SHOW TABLES-- hack
的时节,此时咱们先后实际履行的SQL语句变成了:

<pre>
SELECT uid,username FROM user WHERE username=’plhwin’;SHOW TABLES–
hack’
</pre>

在意:在MySQL中,最后连的有数独减号表示忽略这个SQL减号后面的讲话,我本机的MySQL版本号为5.6.12,目前几有SQL注入实例都是一直以简单独减号结尾,但是事实上测试,这个版号的MySQL要求少单减号后面总得使产生空格才会健康注入,而浏览器是碰头自行删除掉URL尾部空格的,所以我们的流会在点滴单减号后面统一上加任意一个字符或单词,本篇文章的SQL注入实例统一以
-- hack 结尾。

经地方的SQL注入后,原本想要推行查询会员详情的SQL语句,此时尚附加执行了
SHOW TABLES;
语句,这明确不是开发者的原意,此时得当浏览器里看到页面的出口:

<pre>
Array
(
[0] => Array
(
[uid] => 1
[username] => plhwin
)

[1] => Array
    (
        [Tables_in_demo] => user
    )

)
</pre>

君能够清晰的收看,除了会员的音,数据库表的名字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,代码如下:

<?php
if($_POST){
$link = mysql_connect(“localhost”, “root”, “root”);
mysql_select_db(‘demo’, $link);
$username = empty($_POST[‘username’]) ? ” :
$_POST[‘username’];
$password = empty($_POST[‘password’]) ? ” :
$_POST[‘password’];
$md5password = md5($password);
$sql = “SELECT uid,username FROM user WHERE username='{$username}’ AND
password='{$md5password}'”;
$query = mysql_query($sql, $link);
$userinfo = mysql_fetch_array($query, MYSQL_ASSOC);
if(!empty($userinfo)){
//登录成功,打印出会员信息
echo ‘<pre>’,print_r($userinfo, 1),'</pre>’;
} else {
echo “用户称无设有或者密码错误!”;
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset=”utf-8″>
<title>Web登录系统SQL注入实例</title>
</head>
<body>
<form name=”LOGIN_FORM” method=”post” action=””>
签到帐号: <input type=”text” name=”username” value=”” size=30
/><br /><br />
签到密码: <input type=”text” name=”password” value=”” size=30
/><br /><br />
<input type=”submit” value=”登录” />
</form>
</body>
</html>

这儿如输入是的用户名 plhwin 和密码 123456,执行的SQL语句为:

<pre>
SELECT uid,username FROM user WHERE username=’plhwin’ AND
password=’e10adc3949ba59abbe56e057f20f883e’
</pre>

方语句没有其它问题,可以观看页面打印出了登录成功后底会员信息,但要产生捣蛋鬼输入的用户称也
plhwin' AND 1=1-- hack,密码轻易输入,比如aaaaaa,那么拼接之后的SQL查询语句就成为了如下内容:
<pre>
SELECT uid,username FROM user WHERE username=’plhwin’ AND 1=1– hack’
AND password=’0b4e7a0e5fe84ad35fb5f95b9ceeac79′
</pre>

推行方的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-- hackuserinfo.php?username=plhwin' AND 1=2-- hack,如果前者访问能够返正常的信息要后人不克,就着力得以看清是网站在SQL注入漏洞,因为后者的1=2此表达式永远不建,所以就算username传入了不错的参数为无力回天通过,由这可以推测是页面是SQL注入漏洞,并且可以经过username参数进行注入。

怎么守护SQL注入

对此服务器配置范围的防范,应该保证生产环境之Webserver是关闭错误信息的,比如PHP在生育环境的配备文件php.ini中之display_errors应该安装也Off,这样就关闭了错提示,下面我们再次多的于编码的角度来看看如何防止SQL注入。

点用少个实例分析了SQL注入攻击的技能,可以看到,但凡发生SQL注入漏洞的主次,都是盖程序一旦受来自客户端用户输入的变量或URL传递的参数,并且是变量或参数是做SQL语句之一模一样组成部分,对于用户输入的内容还是传递的参数,我们应当使随时保持警惕,这是高枕无忧世界里之「外部数据不可相信」的准,纵观Web安全世界的各种攻击方式,大多数还是因开发者违反了这个条件而招致的,所以本来会体悟的,就是从变量的检测、过滤、验证下手,确保变量是开发者所预期的。

1、检查变量数据类型和格式

假若你的SQL语句是相仿where id={$id}这种样式,数据库里有所的id都是数字,那么就是应当于SQL被实施前,检查确保变量id是int类型;如果是受邮箱,那就应检查并严格管教变量一定是邮箱的格式,其他的品种比如日期、时间等为是一个理。总结起来:如是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去反省,确保变量是咱们预料的格式,这样很十分程度上足避SQL注入攻击。

比如,我们前接受username参数例子中,我们的产品设计应该是当用户注册之一律起,就生出一个用户名的平整,比如5-20个字符,只能由大小写字母、数字以及一些安全的符号组成,不包含特殊字符。此时咱们当发生一个check_username的函数来拓展合并之检查。不过,仍然有不少例外情况并无克运用至当下无异于轨道,比如文章披露网,评论系统等必须要允许用户提交任意字符串的场面,这就是待以过滤等其余方案了。

2、过滤特殊符号

于无法确定固定格式的变量,一定要开展特殊符号过滤或转义处理。以PHP为条例,通常是运用addslashes函数,它见面当指定的预约义字符前加加反斜杠转义,这些预定义的字符是:单引号 (') 双引号 (") 反斜杠 (\) NULL

来看2条SQL语句:
<pre>
$uid = isset($_GET[‘uid’]) ? $_GET[‘uid’] : 0;
$uid = addslashes(uid);
$sql = “SELECT uid,username FROM user WHERE uid='{$uid}'”;
</pre>
以及
<pre>
$uid = isset($_GET[‘uid’]) ? $_GET[‘uid’] : 0;
$uid = addslashes(uid);
$sql = “SELECT uid,username FROM user WHERE uid={$uid}”;
</pre>

地方两个查询语句都由此了php的addslashes函数过滤转义,但以安全性及可大不相同,在MySQL中,对于int类型字段的格查询,上面只告知词之询问功能了平等,由于第一句SQL的变量被单引号包含起来,SQL注入的早晚,黑客面临的主要问题是必要先关前面的单引号,这样才能够如后面的语作为SQL执行,并且还要注释掉原SQL语句被的后边的单引号,这样才得成功注入,由于代码里用了addslashes函数,黑客的攻击会无从下手,但次句子没有因此引号包含变量,那黑客也毫无考虑去关掉、注释,所以即使相同使用addslashes转义,也或存在SQL攻击漏洞。

于PHP程序+MySQL构架的次,在动态的SQL语句被,使用单引号把变量包含起来相当addslashes函数是承诺本着SQL注入攻击的管用手段,但眼看做的尚不够,像上面的2长条SQL语句,根据「检查数据类型」的规范,uid都应当通过intval函数格式为int型,这样不但能够有效避免第二长长的告句之SQL注入漏洞,还会令程序看起再次当,尤其是于NoSQL(如MongoDB)中,变量类型一定要是跟字段类型相匹配才可以。

起地方可以看,第二独SQL语句是来尾巴的,不过鉴于用了addslashes函数,你晤面发现黑客的口诛笔伐语句也设有未可知用特殊符号的规范限制,类似where username='plhwin'如此这般的口诛笔伐语句是迫于执行的,但是黑客可以用字符串转为16进制编码数据或采取char函数进行转账,同样会达相同的目的,如果对当下有些情节感兴趣,可以点击这里翻。而且由于SQL保留重要字,如「HAVING」、「ORDER
BY」的存在,即使是依据黑白名单之过滤方法还会发或多或者掉问题,那么是否还有其它措施来防御SQL注入呢?

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

MySQL的mysqli使提供了预编译语句之支持,不同的程序语言,都分别发生以预编译语句之方法,我们这里依旧为PHP为条例,编写userinfo2.php代码:

<pre>
<?php
header(‘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=?”;
$stmt = $mysqli->prepare($sql);
//绑定变量
$stmt->bind_param(“s”, $username);
$stmt->execute();
$stmt->bind_result($uid, $username);
while ($stmt->fetch()) {
$row = array();
$row[‘uid’] = $uid;
$row[‘username’] = $username;
$userinfo[] = $row;
}
}
echo ‘<pre>’,print_r($userinfo, 1),'</pre>’;
</pre>

从今上面的代码可以视,我们先后里连无下addslashes函数,但是浏览器里运行
http://localhost/test/userinfo2.php?username=plhwin' AND 1=1-- hack里得不至其他结果,说明SQL漏洞以这次里连无在。

其实,绑定变量使用预编译语句是防备SQL注入的超级艺术,使用预编译的SQL语句语义不会见时有发生变更,在SQL语句被,变量用问号?代表,黑客就是本事再杀,也无法改变SQL语句之构造,像面例子中,username变量传递的plhwin' AND 1=1-- hack参数,也只见面当作username字符串来解释查询,从根本上杜绝了SQL注入攻击的发出。

数据库信息加密安全

深信不疑大家还还对2011年爆出的CSDN拖库事件记忆犹新,这桩工作导致CSDN处在风口浪尖被大家痛骂的由即在于他们还是公开存储用户的密码,这吸引了科技界对用户信息安全更是密码安全的醒目关注,我们以防范SQL注入的发的而,也应未雨绸缪,说不定下一个被拖库的便是若,谁知道为。

每当Web开发被,传统的加解密大致可分为三种:

1、对如加密:即加密方和解密方都运同一之加密算法和密钥,这种方案的密钥的保存好关键,因为算法是光天化日之,而密钥是保密的,一旦密匙泄露,黑客还是可自由解密。常见的相得益彰加密算法有:AESDES等。

2、非对如加密:即采用不同的密钥来拓展加解密,密钥被分为公钥和私钥,用私钥加密的数量必须使公钥来解密,同样用公钥加密的数要用相应的私钥来解密,常见的未对如加密算法来:RSA等。

3、不可逆加密:利用哈希算法使数据加密之后无法解密回原数据,这样的哈希算法常用之发:md5SHA-1等。

以我们地方登录系统的示范代码中,$md5password = md5($password);由立句代码可以看来下了md5的不可逆加密算法来储存密码,这也是差不多年来业界常用的密码加密算法,但是就依然未安全。为什么也?

当下是因md5加密有一个特征:同样的字符串经过md5哈希计算后转的加密字符串也是同样的,由于业界采用这种加密的道长期,黑客们为准备了协调强大的md5彩虹表来接向匹配加密前的字符串,这种用于逆向反推MD5加密的彩虹表在互联网及随处可见,在Google里使用md5 解密作为重大词搜索,一下就能找到md5于线破解网站,把咱插入用户数据上的MD5加以密字符串e10adc3949ba59abbe56e057f20f883e填入进,瞬间便能得到加密前的密码:123456。当然为并无是每一个且能学有所成,但足一定之凡,这个彩虹表会越来越全面。

因此,我们来迫切的求使重复好的不二法门对密码数据开展不可逆加密,通常的做法是吗每个用户确定不同的密码加盐(salt)后,再混合用户的真密码进行md5加密,如以下代码:

<pre>
//用户注册上装的password
$password = $_POST[‘password’];
//md5加密,传统做法直接拿加密后的字符串存入数据库,但就不够,我们继承改进
$passwordmd5 = md5($password);
//为用户生成不同之密码盐,算法可以依据自己事务的要而各异
$salt = substr(uniqid(rand()), -6);
//新的加密字符串包含了密码盐
$passwordmd5 = md5($passwordmd5.$salt);
</pre>

小结

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