如何将参数绑定到未准备好的查询?

Posted

技术标签:

【中文标题】如何将参数绑定到未准备好的查询?【英文标题】:How do you bind parameters to a query that isn't prepared? 【发布时间】:2012-08-11 03:20:31 【问题描述】:

我正在制作一个小型 Web 应用程序,它将定期接收用户输入的数据。在研究如何确保首先清理数据输入时,似乎准备好的语句是要走的路。

我找到了this SO question,但是,由于我的应用程序(至少据我所知)每个页面请求不会执行多个查询,看来我真正需要的只是值的绑定到查询中的参数。

我一直在查看有关 PDO 和 mysqli 的 php 手册,但我找不到任何将值绑定到普通查询的示例。我发现的所有示例在绑定之前都有一个$stmt->prepare

语句是否“准备好”是由数据库的支持决定的,而准备语句总是在代码中?或者有没有办法将参数直接绑定到$dbh->query(...)

要解释为什么我要查看是否可以不使用准备,是由于我在帖子前面链接的 SO 问题中的这个陈述:

什么时候不使用准备好的语句?当你只打算在数据库连接消失之前运行一次语句时。

什么时候不使用绑定查询参数(这实际上是大多数人使用准备好的语句来获取的)?

还有这个

我个人不会打扰。伪准备语句可能对它们可能提供的安全变量引用有用。

【问题讨论】:

你能解释一下为什么你想跳过prepare()吗? 为什么人们不给出理由就投反对票?这个网站上的另一个问题让我首先相信两者之间存在区别。 【参考方案1】:

如何将参数绑定到未准备好的查询?

你没有。带参数的 SQL 字符串(即特定位置的问号)需要先被解析(即准备),然后这些问号才能被视为参数值的插入点。

因此,您始终需要先致电prepare(),然后才能致电bind()


参数化语句是包含 SQL 和占位符标记的字符串(例如问号,但不同的数据库使用不同的占位符):

$sql = "SELECT user_id FROM user WHERE user_name = ?"

现在假设您要在此位置插入一个

$_POST["username"]

准备陈述,广义上讲,赋予问号特殊含义“可以在此处插入值”。换句话说,它从占位符创建参数

$stmt->prepare($sql)

将值绑定到参数将参数设置为特定值。

$stmt->bind_param("s", $_POST["username"])

现在可以执行查询,而无需 SQL 字符串和用户提供的值实际相互接触。 这是重要的一点: SQL 和参数值分别发送到服务器。他们从不互相接触。

$stmt->execute();

优点是:

您可以将新值绑定到参数并再次执行查询,而无需重复所有操作(在循环中很有用)。 SQL 注入是不可能的,无论$_POST["username"] 包含什么值。

【讨论】:

那么prepared statements和绑定查询参数没有区别吗? (就我写的代码而言) @sicks 嗯,是什么让你这么想?查看修改后的答案。【参考方案2】:

你没有。原因如下:

如果使用$dbh->query(...) 您可以使用插入到 sql 字符串中的参数调用 sql。通过使用类似的查询

$dbh->query("INS INTO MY_TABLE_OF_NAMES ('$name');"); 

大约 10 年前,大多数 sql 都是这样完成的。这是调用数据库的最直接的方式,使用 RDMS 已经实现的 SQL 接口,不需要特殊的低级接口。但是人们发现这很危险,因为有一种叫做 sql injection 的东西。

http://en.wikipedia.org/wiki/Sql_injection

最简单和最常见的例子是这样的。假设您的网页中有一个将运行的 sql 调用:

 INS INTO MY_TABLE_OF_NAMES VALUE ('$name');

但随后有人会来到您的网站并在那里输入名称bob'); DROP TABLE MY_TABLE_OF_NAMES;

你的内插sql语句突然变成了

INS INTO MY_TABLE_OF_NAMES VALUE ('bob'); DROP TABLE MY_TABLE_OF_NAMES; );

随后会将 bob 插入到您的数据库中,删除您的所有姓名,并在您的网站运行时为结尾的 ); 引发错误。

因此,发明了准备好的语句。它不会将字符串直接插入到您的字符串中,而是使用? 字符来表示动态值,并使用bind 函数安全地插入字符串。这样,您的数据库引擎将永远不会将恶意输入解释为 SQL 代码,并且您的站点不会被欺骗去做它不想做的事情。准备命令需要一个 sql 字符串,需要一些 sql 并半编译成较低级别的数据库语言,在使用 ? 的地方留下动态字符串的空格。然后 Bind 获取其中一个开放空间并用一段数据填充它,编码为转义的 ascii,这样它就不会被误解为 SQL 代码。一旦所有这些?s 都被填满,sql 就可以发送到 RDMS 以运行了。

因此,要回答您的问题,您永远不会将参数绑定到简单查询。如果你想在一个简单的查询中使用动态变量,你只需将它们插入到 SQL 字符串中。但这很危险。准备好的语句允许您预编译 sql 语句,然后安全地将动态参数绑定到它以创建安全的动态 SQL。绑定到 sql 纯粹是准备好的语句的构造。

【讨论】:

+1 表示您的最后一句话。这几乎是我正在寻找的确认。希望我能设置两个正确的答案【参考方案3】:

为了使用绑定参数,您必须使用准备好的语句。这正是目前在 pdo 和 mysqli 中实现的方式。我不确定某些数据库产品是否支持某种类型的通信协议,其中参数化 sql(使用占位符的 sql 文本)与参数值一起发送,而无需先进行显式准备调用,但 pdo 和 mysqli 不会公开此功能(如果可用)。这肯定会成为网络应用程序的一个受欢迎的功能。

使用pdo,是的,当您调用$dbh->prepare($sql) 时,sql 语句是否实际准备好取决于数据库支持。当数据库不支持准备好的语句时,pdo 将模拟它们,或者如果配置为这样做,它总是可以模拟它们。事实上,pdo 默认模拟 mysql 驱动程序的预处理语句,并且默认情况下这样做了很长时间。它通过创建动态 sql 来模拟它们,引用值,就像你一样。在这种情况下,当您调用$stmt->execute() 时,sql(最终值嵌入到文本中)被发送到数据库。是的,这里在某些情况下可以进行sql注入。

【讨论】:

以上是关于如何将参数绑定到未准备好的查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用准备好的语句动态绑定参数?

没有绑定参数的mysqli准备好的语句仍然安全吗?

如何确定从我的 html 表单获取的输入值的数据类型,以便我可以使用它们在准备好的语句中绑定参数

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

如何在 PHP 中动态绑定 mysqli bind_param 参数?

如何在 PHP 中动态绑定 mysqli bind_param 参数?