我们应该总是绑定我们的 SQL 语句吗?

Posted

技术标签:

【中文标题】我们应该总是绑定我们的 SQL 语句吗?【英文标题】:Should we always bind our SQL statements? 【发布时间】:2014-06-24 01:10:47 【问题描述】:

我一直在研究 PDO 的 bindValue()。我知道使用 PDO 准备我的 SQL 语句可以防止 SQL 注入发生。

代码示例:

$stmt = $dbh->prepare('SELECT * FROM articles WHERE id = :id AND title = :title');
$stmt->bindValue(':id', PDO::PARAM_INT);
$stmt->bindValue(':title', PDO::PARAM_STR);
$stmt->execute();

通过将 ID 绑定为一个数字,而 Title 是一个字符串,我们可以限制当有人试图在代码中进行 SQL 注入时造成的损害。

我们是否应该始终将我们的值与PDO::PARAM_ 绑定,以便我们可以限制在 SQL 注入中可以从数据库中提取的内容?在执行我们的bindValue() 时,这是否会增加 PDO 的安全性?

【问题讨论】:

如果您不将 LIMIT 中使用的参数绑定为 int,则查询不会像您想象的那样做出反应(我不记得它是失败还是忽略它)。 ...而不是什么?其他不是bindValue的绑定方法?离开我们的PDO::PARAM_ 论点?或者完全放弃prepare API? 我很确定 OP 的含义如下:“我是否需要使用PDO::PARAM_INT 作为数字,或者我可以使用默认的PDO::PARAM_STR 生存,如果我不这样做的话'不选择参数类型?我知道它会在数字周围添加',但这真的很重要吗?“。 @h2ooooooo 这基本上是个问题,但你的措辞比我写的方式好。我不明白为什么你会希望一个 int 成为一个刺痛。可能是我思维太逻辑了。 @Fluorocarbon 您可能会从阅读mysql - quote numbers or not? 中受益。也就是说,我总是倾向于使用我自己的 DB 包装器来检查 php 类型,然后选择正确的 PARAM 类型。 (***.com/a/22235348/247893) 【参考方案1】:

一个问题有两个问题。重要的是不要混淆它们

    我们是否应该始终使用占位符来表示查询中的变量数据? 我们是否应该始终在应用程序代码中使用某些函数来遵循上述规则? 另外,从comments under the opening post的澄清可以看出第三个问题: 我们应该一直使用第三个参数,还是让PDO默认将所有参数绑定成字符串就可以了?

1。对于第一个问题,答案是绝对肯定的 - YES。

而对于第二个,为了代码的完整性和干燥性 -

2。尽可能避免手动绑定。

有很多方法可以避免手动绑定。其中一些是:

ORM 是简单 CRUD 操作的绝佳解决方案,并且必须在现代应用程序中使用。它将完全隐藏 SQL,在幕后进行绑定:

$user = User::model()->findByPk($id);

Query Builder 也是一种方法,它在一些 PHP 运算符中伪装 SQL,但又在幕后隐藏了绑定:

$user = $db->select('*')->from('users')->where('id = ?', $id)->fetch();

一些抽象库可能会通过 type-hinted-placeholders 处理传递的数据,再次隐藏实际的绑定:

$user = $db->getRow("SELECT * FROM users WHERE id =?i", $id);

如果您仍以上个世纪的方式使用 PHP,并且在整个代码中都有原始 PDO - 那么您可以在 execute() 中传递变量,仍然可以为自己节省大量输入:

$stmt = $dbh->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id]);
$user = $stmt->fetch();

截至第三个问题 - 只要您将数字绑定为字符串(但不是相反!) -

3。用mysql没问题,几乎每个参数都作为字符串发送

因为 mysql 将始终将您的数据转换为正确的类型。我知道的唯一情况是 LIMIT 子句,您不能将数字格式化为字符串 - 因此,唯一相关的情况是when PDO is set in emulation mode and you have to pass a parameter in LIMIT clause。在所有其他情况下,您可以省略第三个参数,以及对bindValue() 的显式调用,没有任何问题。

【讨论】:

【参考方案2】:

您绝对应该使用prepare API 并将值与查询分开传递,而不是进行纯字符串插值(例如"SELECT * FROM foo WHERE bar = '$baz'"不好)。

对于绑定参数,您有三种选择:

bindParam bindValue execute

您使用其中的哪一个并不重要,它们都同样安全。有关差异的一些详细信息,请参阅这些答案:

Confusion between bindValue() and bindParam()? Using PDO without binding

当使用bindParambindValue 时,传递第三个PDO::PARAM_ 参数类型是可选的。如果你不传递它,它默认将参数绑定为字符串。这意味着您最终可能会得到一个等效于... WHERE foo = '42' 而不是... WHERE foo = 42 的查询。这取决于您的数据库将如何处理它。 MySQL 会根据需要自动将字符串转换为数字,就像 PHP 所做的那样(例如在'42' + 1 中)。其他数据库可能对类型更挑剔。

同样,所有选项都同样安全。如果您尝试使用PDO::PARAM_INT 绑定字符串'foo',则该字符串将被强制转换为整数并相应地绑定为值0。没有注射的可能。

【讨论】:

【参考方案3】:

是的,您应该始终将参数与准备好的语句绑定。 它更安全,并且限制了 SQL 注入。但这并不是查询参数时必须做的唯一想法:需要正确的类型控制,最好将行映射到对象中,并在其中包含无效数据时抛出异常。

希望对我有用!

【讨论】:

【参考方案4】:

是的,绑定是要走的路。或参数化查询,这是一个更通用的术语。

@Theo does a wonderful job explaining why parameterized queries is the way to go

您也可以使用stored procedures 来获得额外的安全性,但如果您将一个应用程序用于一个数据库,那就太过分了。它适用于一个数据库的多个应用程序,以确保处理数据时的一致性

【讨论】:

什么是“附加”安全性以及存储过程如何提供它? @YourCommonSense IDK 为什么投反对票,因为我的回答没有任何问题只是因为评论部分可能会缺少一些信息或有点模糊......通过额外的安全性,你可以通过对您的数据执行检查来强制将类型数据输入到您的数据库中。 SQL 注入不是 SQL 中唯一的安全问题... 我不明白应用于安全性的“附加”一词。如果您的代码是安全的 - 那么就不需要“添加”。如果不是 - 那么这些“添加”也不能保证您的安全。我见过真正使用存储过程作为防止注入的保护措施的人太少了。但我见过很多人只是告诉彼此使用它们。 @YourCommonSense 如果您有多个应用程序将数据插入单个数据库。你如何控制数据?如果一个应用程序插入格式错误的数据会导致其他应用程序无法正常工作怎么办?如果你使用存储过程,你有一个集中的方式来检查通过/被插入的数据类型,以确保它不是 mallformed 因为没有它,您的答案只会成为其他答案的链接。实际上,这是不同的问题。

以上是关于我们应该总是绑定我们的 SQL 语句吗?的主要内容,如果未能解决你的问题,请参考以下文章

SQL 查询总是先执行SELECT语句吗?你们都错了!

sql优化

预编译sql语句就sql绑定变量吗

预编译sql语句就sql绑定变量吗

idea的database面板怎么导入sql语句

可以将绑定变量连接到动态 SQL WHERE 子句以添加 AND 语句吗?