以下是一段普普通通的登录演示代码,该脚本需要username和password两个参数,该脚本中sql语句没有任何过滤,注入起来非常容易,后续部分将逐步加强代码的防注入功能。
<?php
include 'config.php';
$username = $_POST['username'];
$password = $_POST['password'];
if(!empty($username) && !empty($password))
{
$conn = new mysqli($db_server,$db_user,$db_pass,$db_name);
if(!$conn)
die('数据库连接失败!<br/>');
$sql = "select * from users where username='{$username}' and password='{$password}'limit 1";
$result = $conn->query($sql);
if($result->num_rows==1){
echo "<script>alert(\"登录成功!\")</script>";
}
else{
echo "<script>alert(\"用户名或密码错误!\")</script>";
}
}
else{
header("Location:/index.php?display=1");
}
?>
针对上面的代码进行sql注入的例子:
username='or''='
password='or''='
或者
username='or''=' limit 1#
password=任意非空值
除了上述的payload,还有很多其他的payload可用。
如何将上述代码加强一下呢?上述代码在进行查询时同时查询了username和password,查询时用户能操作的参数越多,不确定性就越大。可以换一种思路,查询时拼接的字符串只用到主键username,后面在检查password和数据库中的是否一致。即,可以调整查询的结构,减少用户可控的参数拼接。
数据库中密码明文不太好,顺便md5处理一下,加盐效果更好,可以防止数据库被黑了导致敏感信息泄漏。
$password = md5($_POST['password']);
if(!empty($username) && !empty($password))
{
$conn = new mysqli($db_server,$db_user,$db_pass,$db_name);
if(!$conn)
die('数据库连接失败!<br/>');
$sql = "select * from users where username='{$username}' limit 1";
$result = $conn->query($sql);
if($result->num_rows==1){
$row = mysqli_fetch_assoc($result);
if($row['password']==$password)
echo "<script>alert(\"登录成功!\")</script>";
else
echo "<script>alert(\"用户名或密码错误!\")</script>";
}
else{
echo "<script>alert(\"用户名或密码错误!\")</script>";
}
}
这样做的话如果继续用username=‘or‘‘=‘显然是不可以了,除非你知道数据库中第一个用户的密码。或者是使用但是毕竟还是可以破解,因此可以在借助过滤函数来帮忙。在这个例子中,由于username参数两侧是单引号,如果构造sql注入一定需要加入额外的单引号来破坏原语句,所以可以直接借助addslashes()函数将username中的单引号转义。
$username = addslashes($_POST['username']);
$password = md5($_POST['password']);
在这个最简单的例子中,经过这样简单的修改似乎已经没有办法注入了。后面会给一些其他的例子,并给出一些新方法来防御sql注入。
之前提到了过滤函数,用到的是PHP自带的转义函数,但是这个有时候是不够用的。这种情况下可以自定义过滤函数。
后面还要用到预编译。
原文:https://www.cnblogs.com/kevinbruce656/p/12363813.html