我想知道为什么我的代码容易受到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'
如果输入了该信息,则会在不应该显示信息时显示信息。
答案 0 :(得分:4)
您直接将外部数据插入查询中。考虑一个简单查询的这两种变体:
SELECT foo FROM bar WHERE baz=$_GET[id]
SELECT foo FROM bar WHERE baz=:id
第一个是“经典”的传统PHP。 “Golly gee 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
来自某个外部来源 - 当您创建查询字符串时,所有这些元信息都会丢失。
如果您使用预准备语句,那么数据库知道:id
是一个将要使用“外部”数据的地方,它可以保持外部数据与查询本身完全分离,从不允许外部数据的内容会影响查询的含义。怎么样?因为它是一个占位符 - 它是一个直接的“用头beat beat击败数据库服务器”,该数据说“这是外部数据传递的地方。穿上你的生物危害套装并仔细对待”。
答案 1 :(得分:-1)
那是因为你没有真正使用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
已经包含此注入代码。
答案 2 :(得分:-1)
我设法解决了这个问题,我误读了PDO手册页并且没有完全理解执行正确并认为它应该有我的列名而不是表单值,这是它实际需要的。以下是我改变的行:
$query = "SELECT * FROM users WHERE username ='$id'";
到此
$query = "SELECT * FROM users WHERE username =?";
其次
$data->execute(array(':username'));
到此
$data->execute(array($_GET['id']));
这解决了错误,如果,告诉我该网站是否仍然不安全