【问题标题】:Do prepare statements secure your database?准备语句是否保护您的数据库?
【发布时间】:2011-08-24 13:09:45
【问题描述】:

我知道你们中的一些人可能会结束这个问题,但我的问题是由你们和你们的回答提出的。我正在阅读过去两个小时的 SQL 注入问题和答案以及如何保护您的数据库。我看到的大量网页和教程也是如此。

我发现一半的人声称 prepare 语句确实保护了您的数据库,而另外 50 人声称它不是。

另一方面,我读到 mysql_real_escape_string 可以完成这项工作,而其他人则说不是。

我的问题是该相信谁?

另外,这是一个正确的准备语句吗?

$stmt = $dbh->prepare("SELECT phpro_user_id, phpro_username, phpro_password FROM phpro_users 
                    WHERE phpro_username = :phpro_username AND phpro_password = :phpro_password");

        /*** bind the parameters ***/
        $stmt->bindParam(':phpro_username', $phpro_username, PDO::PARAM_STR);
        $stmt->bindParam(':phpro_password', $phpro_password, PDO::PARAM_STR, 40);

        /*** execute the prepared statement ***/
        $stmt->execute();

【问题讨论】:

  • 我还想补充一点,防止 SQL 注入只是“保护数据库”的一部分。保护您的数据库还需要正确设置权限、字符串密码、锁定对服务器的访问权限,以及可能不会立即想到的其他几个因素。

标签: php mysql sql


【解决方案1】:

两者兼而有之。当且仅当您以正确的方式使用它们时,准备好的语句将保护您免受 SQL 注入。例如,如果您仍在为表/列名称插入变量,那么仅“使用”准备好的语句将无济于事。

$stmt = "SELECT * FROM $table WHERE $column = ?"; //not good...

【讨论】:

  • 嗨 Mchl 我用我正在处理的准备语句更新了我的问题。这是正确的方法吗?
  • +1 - 迄今为止的最佳答案,尤其是“如果您以正确的方式使用它们”。
  • 这其实有点用词不当。尽管您通常可以同时完成查询的准备参数化,但它们并不是一回事。确保查询完整性的是参数化。出于这个原因,我投票支持昆汀的回答。
  • 是的。您正在为将要放入语句的所有值使用占位符。在 MySQL 表/列名(除其他外)不能用占位符代替。有些人使用变量插值来动态创建这样的语句,却忘记了即使使用了准备好的语句,他们也应该转义这些变量。
  • Dems:我同意,它们不是一回事,但人们通常认为它们实际上是。还有这种神奇的思维形式是“准备好的语句是 SQL 注入证明”,或者更糟糕的是“PDO 是 SQL 注入证明”。
【解决方案2】:

准备好的语句没有。只要您所有不受信任的数据都通过参数传递而不是插入到语句中,绑定参数就可以保护语句(而不是整个数据库)。人们在使用准备好的语句时,也几乎总是使用绑定参数,因此这两个名称经常混为一谈。

  1. 准备声明
  2. 以变量作为附加参数运行语句

mysql_real_escape_string 几乎总是能完成这项工作,但由于它在流程中增加了额外的步骤,因此更容易出现人为错误。

  1. 转义每个变量
  2. 将变量连接到 SQL 语句中
  3. 运行语句

【讨论】:

  • +1:准备!=参数化。通常你可以同时做这两件事,但这并不意味着它们是一回事。参数化可让您确保查询的完整性。
【解决方案3】:

这是一个很好的讨论。您的问题假设有一种技术可以“保护您的数据库”。事实上,没有一种技术对所有情况都是最好的。所以你需要学会在不同的情况下使用多种解决方案。

  • 转义文字值
  • 准备好的查询中的参数占位符
  • 白名单地图

请参阅我的演示文稿SQL Injection Myths and Fallacies,其中详细介绍了防御 SQL 注入所需了解的一切。

我还在我的书 SQL Antipatterns: Avoiding the Pitfalls of Database Programming 中介绍了 SQL 注入。

【讨论】:

    【解决方案4】:

    在某些情况下无法使用准备好的语句。例如,当您必须动态生成IN() 子句的内容时,如果您已动态选择逗号分隔值进入IN(),则不能执行WHERE col IN (?)。此外,如果您需要在 SELECT 子句中动态生成列列表,则必须通过构建 SQL 字符串来实现。

    底线是,两者都有自己的位置。准备好的语句非常适合预定查询或必须多次执行的查询。转义动态 SQL 在以下情况下非常出色:1) 您必须具有最大的灵活性,并且 2) 您不要忘记转义所有输入

    【讨论】:

    • 为什么不能WHERE col IN (?)?我们在这里做了很多,没有任何问题......
    • @Fatal 在什么 RDBMS 中?您可以使用WHERE col IN (?,?,?) 了解具体情况,但不能将值字符串1,2,3,6,4,8,9 绑定到单个?
    • Oracle 数据库。是的,您必须调整 ? 的计数以匹配您设置的值的数量。当然。但这不是问题,不是吗?
    • 不是真正的问题,但如果你想重用你准备好的语句,那就太可惜了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 2017-06-12
    • 2016-04-05
    • 1970-01-01
    • 1970-01-01
    • 2010-12-19
    相关资源
    最近更新 更多