本人菜鸟一只,懂的不多(毕竟还在临时版本....),只是发表一下自己的理解,说的不对不好的地方,希望各位不要怪罪!如果能给予以指正,更是万分感谢!谢谢~
sql注入是目前公认的危害比较大的一个漏洞了,主要是利用的sql语句处理以及字符过滤不当的缺陷,今天以php为例简要说一下sql注入的一些方法:
数字型注入
php中一般比较常用有sql连接是使用mysql_connect这种面向过程的函数来进行连接,一个简单的例子(此处不考虑规范)
<?php
$c = mysql_connect("localhost","root","123456");
if(!$c){
echo "connect error....".mysql_error();
exit();
}
mysql_select_db("test");
$id = isset($_GET['id'])?$_GET['id']:1;
$sql = mysql_query("select * from test where id={$id}");
if(!$sql){
echo mysql_error();
exit;
}
while($row = mysql_fetch_array($sql)){
echo "id:".$row['id']."<br/>";
echo "content:".$row['content']."<br/>";
}
在这段程序里,他接受了一个用户输入的id参数,通过id来查询数据库中的对象,大体结果如下:
由于没有经过过滤,说明完全信任了用户的输入,而此时有不法用户输入了一些可以干扰原语句执行的字符,如一个单引号 ‘,就会产生如下效果:
这说明我们人为加入的单引号已经打乱了其原有的结构,我们再接着输入or 1=1–
可以看到所有结果都列出来了,再接着判断,输入and 1=2–
可以看到页面不显示任何东西了,因为这是个永假的语句,这样,我们就基本可以断定这是个注入点,我们使用sqlmap来跑一下看看
我们所有的数据库都被列举了出来,可见sql注入的危害性,下面我们来试着对其进行防护:
php中内置了字符串过滤参数可以过滤字符串,函数为addslashes()和htmlspecialchars(),我们试试对数字型的过滤,看可不可以
<?php
$c = mysql_connect("localhost","root","123456");
if(!$c){
echo "connect error....".mysql_error();
exit();
}
mysql_select_db("test");
$id = isset($_GET['id'])?$_GET['id']:1;
$id = addslashes(htmlspecialchars($id));
$sql = mysql_query("select * from test where id={$id}");
if(!$sql){
echo mysql_error();
exit;
}
while($row = mysql_fetch_array($sql)){
echo "id:".$row['id']."<br/>";
echo "content:".$row['content']."<br/>";
}
结果显示,依然无压力:
既然我们这个参数只接受数字类型,我们可以设置个过滤,把一些不必要的参数拦截:
<?php
$c = mysql_connect("localhost","root","123456");
if(!$c){
echo "connect error....".mysql_error();
exit();
}
mysql_select_db("test");
$id = isset($_GET['id'])?$_GET['id']:1;
if(preg_match("/[a-zA-Z]|\W/", $id)){
exit("input error!");
}
$sql = mysql_query("select * from test where id={$id}");
if(!$sql){
echo mysql_error();
exit;
}
while($row = mysql_fetch_array($sql)){
echo "id:".$row['id']."<br/>";
echo "content:".$row['content']."<br/>";
}
sqlmap结果
可以看到,sqlmap使用普通注入已经找不到漏洞了,这里因为我们接收的是数值型的,所以我们可以匹配是否有字母或者字符,如果有,就禁止执行,这样可以达到防注入的效果。
字符型注入
下面我们将之前的脚本更改一下
<?php
$c = mysql_connect("localhost","root","123456");
if(!$c){
echo "connect error....".mysql_error();
exit();
}
mysql_select_db("test");
$content = isset($_GET['content'])?$_GET['content']:"test1";
$sql = mysql_query("select * from test where content='$content'");
if(!$sql){
echo mysql_error();
exit;
}
while($row = mysql_fetch_array($sql)){
echo "id:".$row['id']."<br/>";
echo "content:".$row['content']."<br/>";
}
我们用sqlmap来跑一下:
肯定是能注入的,下面我们使用addslashes()和htmlspecialchars()来过滤一下
<?php
$c = mysql_connect("localhost","root","123456");
if(!$c){
echo "connect error....".mysql_error();
exit();
}
mysql_select_db("test");
$content = isset($_GET['content'])?$_GET['content']:"test1";
$content = addslashes(htmlspecialchars($content));
$sql = mysql_query("select * from test where content='$content'");
if(!$sql){
echo mysql_error();
exit;
}
while($row = mysql_fetch_array($sql)){
echo "id:".$row['id']."<br/>";
echo "content:".$row['content']."<br/>";
}
使用sqlmap跑出的结果
可以看到普通的注入已经无法注入了,可以说明这两个函数可以在一定程度上阻止sql注入,如果想绕过,基本是不可能的,不过不排除绕过的可能性。
最终方法
在php5.3(或者5.4,记不清了。。。::>_<::)之后的版本,如果使用mysql_connect连接就会出现以下的提示
官方会移除mysql_connect这种函数(php7应该移除了,我也不清楚,没用过),建议我们使用pdo或者mysqli,在这两个中,有一种叫做预处理的东西,我们再结合addslaches()和htmlspecial()这两个函数,可以杜绝sql注入的发生,而且这种方法适对数字型或者字符型的注入都适用,代码如下:
<?php
$host="localhost";
$user="root";
$pass="123456";
$db="test";
$mysqli = new mysqli($host,$user,$pass,$db) or die("connect error....");
$sql="select * from test where content=?";
$stmt = $mysqli->prepare($sql);
$content = isset($_GET['content'])?$_GET['content']:"test1";
$content = addslashes(htmlspecialchars($content));
$stmt->bind_param('s',$content);
$stmt->bind_result($id,$content1);
$stmt->execute();
while ($stmt->fetch()) {
echo "id:".$id."<br/>";
echo "content:".$content1."<br/>";
}
希望大家指出错误!谢谢!
</End>
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)