PDO代码仍然容易受到SQL注入的攻击


PDO code still vulnerable to SQL Injection

我想知道为什么我的代码容易受到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已经包含了这个注入代码。