文章主要以php环境讲述sql注入的修复,从简单到复杂,当然网上也有相应的绕过方法。首先我们写一个存在sql注入的php文件。
<?php
$id =$_GET['id'];
$con=mysqli_connect("localhost","root","root","jj");
$query = "select id,name, pass from user where id = $id";
echo "执行的sql语句为:<br>";
echo ($query)."<br>";
$result = mysqli_query ($con,$query);
if (mysqli_num_rows($result) > 0) {
echo "查询结果为:<br>";
while($row = mysqli_fetch_assoc($result)) {
echo "id:" . $row["id"]."<br>",
"username:" . $row["name"]."<br>",
"password " . $row["pass"]. "<br>";
}
} else {
echo "no information";
}
?>
mysqli_real_escape_string()
函数转义 SQL 语句中使用的字符串中的特殊字符。
下列字符受影响:
- \
- ’
- "
- \x00
- \n
- \r
其实实际过程中,主要还是过滤前三个字符,函数语法如下:mysqli_real_escape_string($ con,$ id);
$con是数据库连接参数,对应的php语句为:
$con = mysqli_connect(“localhost”,“root”,“root”,“db”);
id为接受的参数,也可以是字符串"str"。
我们将函数加在获取id之后 $ id = mysqli_real_escape_string($ con,$id);
我们在执行一下看下效果 '被转换成 ’ sql查询错误
is_numeric()
函数用于检测变量是否为数字或数字字符串。
函数语法 is_numeric( $id ) id为接收的参数也可为字符串
该函数可以基本过滤掉一般的注入语句,如果不是数字型则不带入查询直接结束。
if(is_numeric( $id )) {
$query = "select id,name, pass from user where id = $id";
echo "执行的sql语句为:<br>";
echo ($query)."<br>";
$result = mysqli_query ($con,$query);
if (mysqli_num_rows($result) > 0) {
echo "查询结果为:<br>";
while($row = mysqli_fetch_assoc($result)) {
echo "id:" . $row["id"]."<br>",
"username:" . $row["name"]."<br>",
"password " . $row["pass"]. "<br>";
}
} else
echo "no information";
这个方法的局限在于只能接收数字型参数,如果想使用字符型参数则不能使用这个函数。
preg_replace()
函数执行一个正则表达式的搜索和替换。
函数语法 echo preg_replace (‘a’,‘b’,‘c’) 函数一个三个变量 第一个a是匹配的内容,b是要转换后的内容,c为转换的目标。
通俗一点说就是匹配到就替换,c中如果有包含a内容的字符串,就将其转换成b。下面是过滤一些危险语句的正则替换。
$pregs = '/select|insert|update|CR|document|LF|eval|delete|script|alert|\'|\/\*|\#|\--|\ --|\/|\*|\-|\+|\=|\~|\*@|\*!|\$|\%|\^|\&|\(|\)|\/|\/\/|\.\.\/|\.\/|union|into|load_file|outfile/';
PDO
PDO全名PHP Data Object
PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口。
PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。
PDO可以说是基本杜绝了sql注入,因为它将查询语句与接受的参数分离开来,哪怕输入恶意的sql查询语句,也起不到查询作用。
使用PDO访问MySQL数据库时,真正的real prepared statements 默认情况下是不使用的。为了解决这个问题,你必须禁用 prepared statements的仿真效果。下面是使用PDO创建链接的例子:
$pdo=new PDO("mysql:host=localhost;dbname=db","user","pass");
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
这行代码极其重要,如果没有禁用prepared statements的仿真效果,本地就会将查询的sql语句模板和经过本地转义的参数id 拼接然后发送到mysql进行查询,这还会导致sql注入。
防范sql注入通过预处理的一些方式以及bindParameter()方法绑定参数来防止SQL注入,本文主要举例bindParmeter()方法
<?php
$id =$_GET['id'];
$pdo=new PDO("mysql:host=localhost;dbname=jj","root","root");
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$query = "select id,name, pass from user where id = :id";
echo "执行的sql语句为:<br>";
echo ($query)."<br>";
$stmt=$pdo->prepare($query);
$stmt->bindParam(":id",$id,PDO::PARAM_STR);
$stmt->execute();
$row = $stmt->fetch();
if( $stmt->rowCount() == 1 ) {
echo "查询结果为:<br>";
$id=$row['id'];
$user=$row['name'];
$pass=$row['pass'];
echo 'id:'.$id.'<br>',"name:".$user."<br>","pass:".$pass."<br>";}
else
echo 'no information';
?>
上面这段代码就可以防范sql注入。为什么呢?
当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有查询模板中的:id 发送过去,没有用户提交的数据id;当调用到 execute()时,用户提交过来的值才会传送给数据库,他们是分开传送的,两者独立的,SQL攻击者没有一点机会。我们执行一下看下结果。
我们给id赋值为 1 and 1=2 但发送到mysql的语句仍为select id,name,pass from user where id=:id,随后接收的id再单独发送至数据库。