我想知道为什么我的代码容易受到SQL注入的攻击,尽管我使用PDO准备和执行?
这是我的代码:
$conn = new PDO('mysql:host=localhost;dbname=SQLHack', $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$id = $_GET['id'];
$query = "SELECT * FROM users WHERE username ='$id'";
$data = $conn->prepare($query) or die("ERROR: " . implode(":", $conn->errorInfo()));
$data->execute(array(':username'));
$data->setFetchMode(PDO::FETCH_BOTH);
while ($r = $data->fetch()) {
echo "<br />'n";
print_r("ID: " . $r['id'] . " Username: " . $r['username']);
}
这条线很容易受到攻击,但如果它很容易受到这条线的攻击,它也很容易受到其他许多线的攻击。
' or 1=1 union select 1,2,3'
如果输入了它,它就会在真正不应该显示的时候显示信息。
您直接将外部数据插入到查询中。考虑一下简单查询的这两种变体:
SELECT foo FROM bar WHERE baz=$_GET[id]
SELECT foo FROM bar WHERE baz=:id
第一个是"经典"的传统PHP。"天哪,willikers……我们生活在一个完美的世界里,没有人会攻击服务器。让我们直接将用户提供的数据填充到这个SQL查询中,并在它执行时运行Kumbaya。任何事情都不可能出错"。
第二种是使用占位符的经典防御性编程。在这两种情况下,这个id
的值最终都会被填充到表中的baz
字段中。但它是如何到达那里的完全不同。
考虑一个恶意用户传入一些坏数据:$_GET['id'] = '0 or 1=1'
对于第一个,SQL服务器只需看到:
SELECT foo FROM bar WHERE baz=0 or 1=1
它绝对无法知道0 or 1=1
来自某个外部来源——当您创建查询字符串时,所有的元信息都会丢失。
如果使用预先准备好的语句,那么DB知道:id
是将要使用"外部"数据的地方,并且它可以将外部数据与查询本身完全分离,永远不会让外部数据的内容影响查询的含义。怎样因为这是一个占位符——它是一个直接的"用线索棒打数据库服务器的头"数据,上面写着"这是外部数据的去向。穿上你的生物危害防护服,小心处理"。
我设法解决了这个问题,我误读了PDO手册页,没有完全正确理解执行,并认为它应该有我的列名而不是表单值,这正是它实际需要的。以下是我更改的行:
$query = "SELECT * FROM users WHERE username ='$id'";
到此
$query = "SELECT * FROM users WHERE username =?";
其次是
$data->execute(array(':username'));
到此
$data->execute(array($_GET['id']));
这已经解决了错误,如果,告诉我站点是否仍然不安全
这是因为您并没有真正使用PDO。
$query = "SELECT * FROM users WHERE username = :username";
$data = $conn->prepare($query);
$data->bindValue(':username', $id, PDO::PARAM_STR);
$data->execute();
$data->setFetchMode(PDO::FETCH_BOTH);
代码中的漏洞是因为您将此字符串放入PDO::prepare()中:
SELECT * FROM users WHERE username ='' or 1=1 union select 1,2,3
并且PDO正确地准备它。在PDO工作之前,您的$query
已经包含了这个注入代码。