php - Pdo prepare的语句是否足以防御sql注入?

  显示原文与译文双语对照的内容

假设我有这样的代码:


$dbh = new PDO("blahblah");

$stmt = $dbh->prepare('SELECT * FROM users where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );

PDO文档说明:

提供给预处理语句的参数不需要用引号括起来,驱动程序会处理这些。

我需要做的就是避免SQL注入? 真的这么容易?

你可以假设MySQL有区别。 另外,我真的很好奇对 SQL注入 使用预准备语句。 在这种情况下,我不关心XSS或者其他可能的漏洞。

时间:

准备好的语句/参数化查询足以防止该语句上的一阶注入。 如果你在应用程序的其他地方使用un-checked动态 sql,那么你仍然会受到 ii 注入的攻击。

二阶注入手段之前被包括在查询中的数据已经进行遍历一次数据库,和就更难拔掉电源插头。 AFAIK,你几乎不会看到真正的2和顺序攻击,因为攻击者通常更容易 social-engineer 。

当你可以使一个值存储在以后用作查询中的文本的数据库中时,你可以完成 2 nd顺序注入攻击。 例如假设在网站( 假设这个问题的MySQL DB ) 上创建帐户时,你将以下信息作为新用户名输入:


' + (SELECT UserName + '_' + Password FROM Users LIMIT 1) + '

如果用户名没有其他限制,准备好的语句仍然会确保上面的嵌入查询在插入时不会执行,并在数据库中正确存储该值。 但是,假设应用程序稍后从数据库中检索你的用户名,并使用字符串连接来包含一个新的查询。 你可能会看到别人的密码。 因为用户表中的前几个名字往往是管理员,你也可能刚刚给出了场。 ( 也注意:这是不将密码存储在纯文本中的另一个原因) !

我们看到,那么,预处理语句是讲到这一个单一的查询,但是通过本身,它们不是 足以抵御 SQL注入 攻击要在整个应用程序中,所有对数据库的访问,因为他们缺乏一种机制来强制该应用程序使用安全的代码内。 于求解 SQL注入 相关problem,但是,作为优秀的应用程序设计的一部分使用—这可能会包括实践,比如代码检查或者使用的一个 ORM,数据层或者服务层,用来限制动态 sql — 预处理语句 的是主 tool. 如果遵循良好的应用程序设计原则,使数据访问与程序的其余部分分离,就可以轻松实施或者审计每个查询都使用参数化。 在这种情况下,SQL注入 ( 第一个和第二个顺序) 被完全阻止。

不,他们并不总是。

这取决于是否允许用户输入放入查询本身。 例如:


$dbh = new PDO("blahblah");

$tableToUse = $_GET['userTable'];

$stmt = $dbh->prepare('SELECT * FROM '. $tableToUse. ' where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );

在这里示例中很容易受到SQL注入和使用预准备语句的影响,因为用户输入用作标识符,而不是作为数据。 正确的答案是使用某种过滤/验证,比如:


$dbh = new PDO("blahblah");

$tableToUse = $_GET['userTable'];
$allowedTables = array('users','admins','moderators');
if (!in_array($tableToUse,$allowedTables)) 
 $tableToUse = 'users';

$stmt = $dbh->prepare('SELECT * FROM '. $tableToUse. ' where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );

注意:你不能使用PDO来绑定超出 DDL ( 数据定义语言) 之外的数据,这不能正常工作:


$stmt = $dbh->prepare('SELECT * FROM foo ORDER BY :userSuppliedData');

上面不工作的原因是 DESCASC 不是数据 。 PDO只能对数据进行转义。 其次,你甚至不能在它周围加上 ' 引号。 允许用户选择排序的唯一方法是手动筛选并检查它是 DESC 还是 ASC

不这是不够的( 在某些特定情况下) ! 默认情况下,当使用MySQL作为数据库驱动程序时,PDO使用模拟的准备语句。 使用MySQL和PDO时,你应该总是禁用模拟的准备语句:


$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

另外一个应该做的事情是设置正确的数据库编码:


$dbh = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

也可以看到这个相关问题: 中防止 SQL注入的最佳方法

还要注意的是,只有在显示数据的时候,你才会看到你自己的数据库。 比如 通过使用正确的编码和引号样式再次使用 htmlspecialchars()

就像JimmyJ说的,你仍然需要清理你的数据 ! 使用 PDO,你仍然可以在数据库中保存类似 <?php phpinfo();?>的东西。 好的,这个例子不会损害任何东西,但它可能是其他任何东西而不是 phpinfo 。

...