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注入攻击的暴发。

数据库信息加密安全

信任我们都还对二〇一一年暴露的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、严峻加密处理用户的机密信息。

相关文章