了解 PDO 准备好的语句和绑定参数

Posted

技术标签:

【中文标题】了解 PDO 准备好的语句和绑定参数【英文标题】:Understanding PDO Prepared Statements and Binding Parameters 【发布时间】:2016-09-04 13:10:05 【问题描述】:

根据经验,并且经常被告知使用准备好的语句和绑定我的参数的好处,我一直在我的代码中使用这两种技术,但是我想准确地了解这两种技术的用途:

根据我对准备好的语句的理解:

$sql = "SELECT * FROM myTable WHERE id = ".$id;
$stmt = $conn->prepare($sql);
$stmt->execute();

前面的代码应该使用我提出的查询在数据库中创建一种缓冲区。现在根据我的理解(我可能错了),前面的代码是不安全的,因为字符串 $sql 可以是任何东西,具体取决于 $id 的实际含义,如果 $id = 1; DROP TABLE myTable;--,即使我有准备好的语句,我也会插入恶意查询。

根据我的理解这是绑定我的参数的地方。如果我改为执行以下操作:

$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->execute();

数据库应该事先准确地知道 sql 语句的所有部分: SELECT这些列:*FROM myTableWHERE id =“用户输入的变量”,如果"a variable that was input by the user" != a variable,则查询失败。

有人告诉我我的理解是正确的,而其他人则告诉我这是错误的,如果我错了、正确或遗漏了什么,有人可以告诉我吗?并尽可能详细地说明,非常感谢所有反馈!

【问题讨论】:

第一段代码可以保护您一无所获,因为您仍在将$id 可能来自$_POST['id'] 直接传递到查询中 @RiggsFolly 所以我的假设是正确的,即准备好的语句需要bindParam() 才能保证安全? 您必须使用?,? 或您的第二种机制 好吧,你也可以使用$stmt->execute( [':id' => $id] );,但这基本上是不可见的绑定 也许您还缺少关于准备语句的一点是,如果您想在循环中加载数据,您可以->prepare() 一次和bind; execute() 多次。因此只编译和优化一次查询,但使用它 100 次 【参考方案1】:

你是对的,第一种情况是不安全的。不过,重要的是要理解,只有在使用可变数据和/或重复执行相同的查询时,准备语句才有价值。如果您正在执行没有变量的普通语句,您可以简单地这样做:

$sql = "SELECT * from myTable WHERE this_column IS NOT NULL";
$result = $conn->query($sql);

最后得到一个 PDOStatement 对象,就像你使用 PDO::exec() 时一样。

对于你的第二种情况,你基本上是正确的。发生的情况是传递给数据库的变量被转义和引用(除非您使用 PDOStatement::bindParam() 的第三个参数另外指定,否则它作为字符串发送,这在大多数情况下都可以。)因此,查询不会“失败”如果发送了错误的数据。它的行为就像您传递了一个在数据库中不作为 ID 存在的有效数字。当然,some edge cases 即使使用正确准备的语句,您仍然容易受到攻击。

另外,为了让生活更轻松,您可以使用这样的准备好的语句来进行隐式绑定:

$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->execute([":id"=>$id]);

或者甚至像这样,带有未命名的参数:

$sql = "SELECT * FROM myTable WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->execute([$id]);

当然,在我输入答案时,大部分内容已在 cmets 中进行了解释!

【讨论】:

以上是关于了解 PDO 准备好的语句和绑定参数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用准备好的 PDO 语句设置 ORDER BY 参数?

如何使用准备好的 PDO 语句设置 ORDER BY 参数?

PDO 准备好的语句有多安全

数据库抽象层PDO 8

如何将 pdo 的准备好的语句用于 order by 和 limit 子句?

PDO准备好的语句是否足以阻止SQL注入?