你应该使用准备好的陈述来逃避吗?

时间:2013-05-03 18:40:38

标签: php mysql

我看到很多人说你应该总是使用预准备语句进行数据库查询。但是,PHP文档说:

  

每个预准备语句都占用服务器资源。声明应该   使用后立即明确关闭。如果没有明确地完成,   当语句句柄被释放时,语句将被关闭   PHP。

     

使用预准备语句并不总是最有效的方法   执行一份声明。准备好的语句只执行一次导致   比未准备好的声明更多的客户端 - 服务器往返。

来自http://php.net/manual/en/mysqli.quickstart.prepared-statements.php

鉴于上述情况,如果您只打算使用一次查询,那么使用预准备语句会不是更好?

4 个答案:

答案 0 :(得分:5)

差异被认为可以忽略不计。

然而,必须将原生预备陈述与预备陈述的一般概念区分开来。

前者只是大多数DBMS支持的运行查询的一种形式,解释here。它的用法可以质疑 后者是用占位符代替实际数据的一般概念,意味着进一步处理替代数据。它广泛用于编程,一个着名的printf()函数就是一个例子。并且后一种方法必须始终用于对数据库运行查询,无论它是否由本机预准备语句支持。这是因为:

  • 预备语句可以正确格式化(或处理)不可避免的
  • 预备语句在正确的位置正确格式化(或处理) - 在查询执行之前,而不是在其他地方,因此,我们的安全不会依赖于像这样不可靠的来源
    • 一些PHP'魔术'功能,它可以破坏数据而不是安全。
    • 一个(或几个)程序员的善意,他们可以决定在程序流程的某个地方格式化(或不格式化)我们的变量。这是非常重要的一点。
  • prepared语句影响查询的非常值,但不会影响源变量,源变量保持不变,可以在其他代码中使用(通过电子邮件发送或在屏幕上显示)。
  • 预备语句可以使应用程序代码显着缩短,在幕后进行所有格式化(*仅在驱动程序允许的情况下)。

因此,即使您考虑不使用本机预处理语句(这是非常好的),您也必须始终使用占位符而不是实际数据创建查询。为此,您可以使用PDO,它完全按照上述方式工作 - 默认情况下它只是模拟准备,表示正在创建的查询和数据的常规SQL查询,然后针对数据库运行。

但是,PDO缺乏对许多重要数据类型的支持,例如标识符或数组 - 因此它使您无法始终使用占位符,从而使注入成为可能。幸运的是,safeMysql具有每种数据类型的占位符,并允许您安全地运行查询。

答案 1 :(得分:1)

除非你能够对此进行基准测试并证明它们对性能有可测量的拖累,因为它的速度至少要慢10-15%,所以没有理由对此大惊小怪。即使这样,只要几乎绝对确定数据完整性,这是一个很小的代价,假设你只使用占位符。

准备好的语句,即使只使用一次,也很容易实现正确,如果你从不使用字符串插值来注入数据,那么很难出错。

任何认为自己是专业程序员的人都必须定期审核他们的应用程序,以确保其中没有SQL注入错误。如果事情没有明显地显而易见,那么就不能假定逃脱,所以你必须进行调查。

占位符还使您不太可能错误地混合列及其相关值。 PDO的命名参数功能可以很好地避免这种情况。

答案 2 :(得分:1)

准备语句会生成适合变量的查询计划。然后它可以多次使用。

与他们有关联,顺便说一下。请考虑以下声明:

select * from posts order by post_date limit 10 offset ?;

如果您准备好了,那么您将无法进行索引扫描。因为,对于所有规划人员都知道,你想要最后几行,而且当你浏览索引时,一个接一个地浏览你的数百万个帖子是不可能的。

如果直接使用参数运行,您将获得小偏移的索引扫描,并且没有索引扫描超过阈值,原因与预准备语句不使用索引的原因相同。

有鉴于此,考虑到大多数应用只运行一次查询,您通常可以坚持模拟准备。特别参见PDO::ATTR_EMULATE_PREPARES

答案 3 :(得分:0)

SQL注入是预处理语句首选方式的原因。如果您的查询是常量,那么没有理由使用预准备语句。如果您确定即使通过字符串连接构造查询也是安全的,那么可以跳过预准备语句。

常数没问题。

$sql = "SELECT * FROM foobar";

如果$ id变量不可能包含除int之外的任何其他类型的数据,那么这是可以的。

$sql = "SELECT * FROM users WHERE id=".$id;

通常很难确保变量中包含的数据是正确的类型,因此准备好的语句是构建查询的更安全的方式。