PDO 准备好的语句有多安全

Posted

技术标签:

【中文标题】PDO 准备好的语句有多安全【英文标题】:how safe are PDO prepared statements 【发布时间】:2010-11-21 19:41:05 【问题描述】:

不久前开始使用 PDO 准备语句,据我了解,它为您完成了所有转义/安全。

例如,假设 $_POST['title'] 是一个表单域。

$title = $_POST['title'];
$query = "insert into blog(userID, title) values (?, ?)"
$st = $sql->prepare($query);
$st->bindParam(1, $_SESSION['user']['userID'], PDO::PARAM_INT);
$st->bindParam(2, $title);
$st->execute();

这真的安全吗?我还需要做什么吗?我还需要考虑什么?

谢谢。

【问题讨论】:

【参考方案1】:

严格来说,实际上不需要转义,因为参数值永远不会插入到查询字符串中。

查询参数的工作方式是在您调用prepare() 时将查询发送到数据库服务器,而稍后在您调用execute() 时发送参数值。因此它们与查询的文本形式分开。永远不会有 SQL 注入的机会(前提是 PDO::ATTR_EMULATE_PREPARES 为假)。

是的,查询参数可以帮助您避免这种形式的安全漏洞。

它们是否 100% 证明可以防止任何安全漏洞?不,当然不。您可能知道,查询参数仅代替 SQL 表达式中的单个文字值。您不能让单个参数替代值列表,例如:

SELECT * FROM blog WHERE userid IN ( ? );

您不能使用参数来使表名或列名动态化:

SELECT * FROM blog ORDER BY ?;

您不能将参数用于任何其他类型的 SQL 语法:

SELECT EXTRACT( ? FROM datetime_column) AS variable_datetime_element FROM blog;

因此,在很多情况下,您必须在prepare() 调用之前将查询作为字符串进行操作。在这些情况下,您仍然需要仔细编写代码以避免 SQL 注入。

【讨论】:

LIKE ? 也是有效的,但您应该转义用于匹配的字符。 关于“从来没有机会进行 SQL 注入(假设 PDO::ATTR_EMULATE_PREPARES 为假)。”,这是否意味着 PDO 模拟准备不如数据库驱动程序的本地准备安全?如果有,为什么? 使用 PDO 准备注入的一个很好的例子(由于这个答案中提到的限制)是最近的 drupal 漏洞sektioneins.de/advisories/… @thelastshadow,感谢您提供的真实示例!大家升级你的 Drupal!【参考方案2】:

对 SQL 注入是安全的。

有几件事是不安全的:

拒绝服务(导致创建过多行) 跨站点脚本攻击(如果标题曾经回显给其他用户)

安全不仅仅是防止 SQL 注入。

【讨论】:

请贡献。如果标题回显给另一个用户,你是什么意思? 假设您将博客文章的标题存储在数据库中,其他用户可以查看这些文章。然后是潜在的跨站点脚本攻击,恶意用户可以制作包含 html 的标题,以将恶意脚本嵌入到页面中,因为它显示给您网站的其他用户。【参考方案3】:

关于 SQL 注入,我相信这是您可以获得的最安全的方法,特别是如果您使用像 PDO::PARAM_INT 这样的常量。

【讨论】:

这个“特别”需要 IMO 澄清一下。你可能不想说它是 95% 安全的,但如果你使用常量,它是 100% 安全的。如果没有常量,它不是 100% 安全的,它是不安全的。如果它是 100%,那么使用常量就不是“特别”安全的。使用常量和不使用常量的安全区别是什么?【参考方案4】:

既然提到了XSS,我觉得也可以看看使用诸如输入清理类http://www.phpclasses.org/browse/package/2189.html之类的东西来防止XSS攻击。

【讨论】:

以上是关于PDO 准备好的语句有多安全的主要内容,如果未能解决你的问题,请参考以下文章

PDO 是不是仍在为 MySQL 模拟准备好的语句?

PDO 准备好的语句如何帮助防止 SQL 易受攻击的语句?

使用 PDO 准备好的语句 MySQL 错误

带有位域的 PDO 准备好的语句

为啥使用 mysql 准备好的语句比使用常见的转义函数更安全?

在 PHP 中到处使用准备好的语句? (PDO)