了解 PDO 准备语句和绑定参数
Understanding PDO Prepared Statements and Binding Parameters
根据经验以及不断被告知使用准备好的语句和绑定参数的好处,我一直在我的代码中使用这两种技术,但是我想确切地了解这两种技术中每一种的目的:
根据我对预处理语句的理解:
$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 that was input by the user" != a variable
,则查询失败。
有些人告诉我我的理解是正确的,而另一些人则认为这是错误的,如果我错了、正确了或遗漏了什么,有人可以告诉我吗?并尽可能多地阐述,非常感谢所有反馈!
第一种情况是不安全的,你是对的。不过,重要的是要理解,只有在使用可变数据时准备语句才有价值,and/or 重复执行相同的查询。如果你正在执行没有变量的普通语句,你可以简单地这样做:
$sql = "SELECT * from myTable WHERE this_column IS NOT NULL";
$result = $conn->query($sql);
最后得到一个要使用的 PDOStatement
对象,就像使用 PDO::exec()
.
一样
对于你的第二种情况,同样,你基本上是正确的。发生的事情是传递给数据库的变量被转义和引用(除非你用 PDOStatement::bindParam()
的第三个参数另外指定,它作为一个字符串发送,这在大多数情况下都很好。)所以,查询不会 "fail" 如果发送了错误数据。它的行为就像您传递了一个在数据库中不作为 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]);
当然,大部分内容在我输入答案时已经在评论中解释过了!
根据经验以及不断被告知使用准备好的语句和绑定参数的好处,我一直在我的代码中使用这两种技术,但是我想确切地了解这两种技术中每一种的目的:
根据我对预处理语句的理解:
$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 that was input by the user" != a variable
,则查询失败。
有些人告诉我我的理解是正确的,而另一些人则认为这是错误的,如果我错了、正确了或遗漏了什么,有人可以告诉我吗?并尽可能多地阐述,非常感谢所有反馈!
第一种情况是不安全的,你是对的。不过,重要的是要理解,只有在使用可变数据时准备语句才有价值,and/or 重复执行相同的查询。如果你正在执行没有变量的普通语句,你可以简单地这样做:
$sql = "SELECT * from myTable WHERE this_column IS NOT NULL";
$result = $conn->query($sql);
最后得到一个要使用的 PDOStatement
对象,就像使用 PDO::exec()
.
对于你的第二种情况,同样,你基本上是正确的。发生的事情是传递给数据库的变量被转义和引用(除非你用 PDOStatement::bindParam()
的第三个参数另外指定,它作为一个字符串发送,这在大多数情况下都很好。)所以,查询不会 "fail" 如果发送了错误数据。它的行为就像您传递了一个在数据库中不作为 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]);
当然,大部分内容在我输入答案时已经在评论中解释过了!