了解 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 myTable
和WHERE 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 参数?