1、建立用户表 我用PHPMyAdmin在我test数据库建立了一张user表,表中有三个字段,分别是用户名、密码、邮箱,然后插入了一条测试数据 用户名是:周运金,密码是test(用了MD5加密) 2、建立登陆页面form.html
<html> <head> <title>Sql注入</title> <meta http-equiv="content-type"content="text/html;charset=utf-8"> </head> <body> <form action="validate.php" method="post"> <fieldset > <legend>Sql注入</legend> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密 码:</td> <td><input type="text" name="password"></td> </tr> <tr> <td><input type="submit" value="提交"></td> <td><input type="reset" value="重置"></td> </tr> </table> </fieldset> </form> </body> </html>效果: 3、创建处理登陆Validate.php
<?php //面向对象的连接方式 $mysqli =new mysqli("localhost","root","123","test"); if(!$mysqli ){ echo mysqli_connect_error(); } $mysqli->set_charset("utf8"); $mysqli->query("set names 'utf8'"); $name=$_POST['username']; $pwd=md5($_POST['password']); $sql="select * from user where username='$name' and password='$pwd'"; $query=$mysqli->query($sql); if($query == false){ echo $mysqli->error; } else{ $rows =$query->num_rows; if($rows){ header("Location:manage.php"); }else{ echo "您的用户名或密码输入有误,<a href='form.html'>请重新登录!</a>"; } } $mysqli->close(); ?>这里说明一下,原始MySQL在PHP5.5之后已经被php抛弃,采用面像对象连接的MYSQLI。 4、建立登陆成功的页面manage.php
<?php echo "You are a manager"; ?>这样就完成了一个有SQL注入漏洞的登陆程序了。很明显程序没有对用户输入的数据进行处理就直接放进sql语句里面了。这是很危险的做法。 接下来就开始攻击一下吧,做法其实很简单只要去拼凑sql语句就好了。 我先输入正确的用户名和密码: 为了显示密码我这里没有用密码框了。输入完之后成功登陆 接下来正式注入了,用户名输入:’ or 1=1# 密码随便输入 点击提交之后: 居然登陆成功了。哈哈哈,接下来分析一下注入后SQL语句吧: 我在sql语句那里设置了一个断点,看一下拼接后的sql 这里可以看到拼接后的sql语句是: select * from user where username=” or 1=1#’ and password=’202cb962ac59075b964b07152d234b70’这里的#是mysql的注释符,意思就是忽略后面的sql语句,这样的话就不用验证了,而且在username后面还有一句逻辑语句or 1=1,这样的话这条语句永远成立,所以就通过了验证。
接下来就谈谈常见的方SQL注入方法: 1、最常见的就是采用mysqli_real_escape_string函数进行转义一些特殊的字符比如\n、\r、\、’、” 等(在查找mysql_real_escape_string函数的时候发现PHP文档说这个函数在php5.5之后就被抛弃了,改用mysqli_real_escape_string,看来PHP要全面使用面向对象的mysqli了),就像刚才的注入有个单引号,用了这个函数之后就会被转义,这样拼接就失败了。我们来看看再用之前的’ or 1=1#去拼接,加入这个函数之后会怎样:
$name=mysqli_real_escape_string($mysqli,$_POST['username']); //必须使用数据库连接,这样看来是专门为防sql注入准备的,比较安全 $pwd=mysqli_real_escape_string($mysqli,md5($_POST['password']));结果不能登录了,设个断点看一下转义之后sql语句: 这个是PHP文档给出的例子
<?php // Assume this is a simple comments form with a name and comment. $name = mysqli_real_escape_string($conn, $_POST['name']); $comments = mysqli_real_escape_string($conn, $_POST['comments']); // Here is where most of the action happens. But see note below // on dumping back out from the database // We should use the ENT_QUOTES flag second parameter... $name = htmlspecialchars($name); $comments = htmlspecialchars($comments); $insert_sql = "INSERT INTO tbl_comments ( c_id, c_name, c_comments ) VALUES ( DEFAULT, '" . $name . "', '" . $comments . "')"; $res = mysqli_query($conn, $insert_sql); if ( $res === false ) { // Something went wrong, handle it } // Now output page showing comments ?>看到单引号‘被转义成了/’了这样的话拼接就没用了。 二、打开magic_quotes_gpc来防止SQL注入 这个原理跟第一个的原理类似,是将GET、POST、COOKIE传过来的数据进行自动转义,相当于用addslshes()函数进行转义。但是这种方式没有办法防止当参数是数字型的sql攻击,因为数字是没有单引号或者双引号的。解决的办法是用intaval()函数强制将字符数据转换成数字。如果开启了magic_quotes_gpc=on,在第一个方法中记得用stripslashes函数去掉/ 三、自定义过滤函数 以下是W3C给出的一个过滤函数我将转义函数改了
function check_input($value,$con) { // 去除斜杠 if (get_magic_quotes_gpc()) { $value = stripslashes($value); } // 如果不是数字则加引号 if (!is_numeric($value)) { $value = "'" . mysqli_real_escape_string($con,$value) . "'"; } return $value; }这个函数考虑到使用mysqli_real_escape_string比使用addslshes()更加安全
最后注意了: 在写这篇文章的时候,笔者参考了一篇文章说sql注入可以绕开以上的方法了,吓得得我一身冷汗,看了之后果然觉得厉害。文章中建议使用不要用mysql_query了而是用PDO和MYSQLi来代替mysql_query,心想还好我用的是mysqli,它采用了mysqli的Prepared Statement机制可以有效解决sql注入。大家可以参考一下 文章地址:参考文章
总结: 1、sql注入的方式其实很简单,但是后果却是很致命的,所以开发人员一定记住永远不要去相信客户的数据。 2、防sql注入的方法有很多,但是一定得保持一定得技术更新,因为黑客的技术越来越厉害了,要经常更新这方面的防护知识,随时保持最新的防漏洞知识。