繁体   English   中英

为什么不能在 PDO 准备语句中替换表名的原因是什么?

[英]What is the reasoning for why table names can not be susbstituted in PDO prepared statements?

这按预期工作:

$st = $db->prepare('SELECT COUNT(1) WHERE EXISTS (SELECT * FROM ' . $table_name . ')');
$st->execute(array());

这会引发语法错误:

$st = $db->prepare('SELECT COUNT(1) WHERE EXISTS (SELECT * FROM :tblname )');
$st->execute(array('tblname' => $table_name));

根据评论,“数据只能在可以表示为数字或带引号的字符串文字时才能绑定。表名既不能是数字也不能是字符串文字。”

这背后的原因是什么?

参数不仅仅是字符串替换。 这实际上是他们擅长防止 SQL 注入漏洞的原因。

(好吧,在某些客户端中,它们使用字符串替换来模拟查询参数。如果您设置PDO::ATTR_EMULATE_PREPARES属性,PDO 就会有这种行为。但我将讨论真正的参数。)

参数不是字符串替换的原因是它们在被数据库服务器解析与 SQL 查询组合。 解析步骤验证您的 SQL 语法是否正确,并验证您在查询中命名的表和列是否确实存在。

解析步骤中唯一可以省略的是表达式中的常量值。 解析器看到参数占位符之一并知道该标记的行为就像您使用了单个标量常量值一样。 因此它可以继续进行语法验证。

以下示例不是有效的 SQL 语法,因为您无法从常量字符串中选择:

SELECT COUNT(1) WHERE EXISTS (SELECT * FROM 'mytable' )

但是,如果表名作为参数传递,这就是语法的解释方式。 SQL 引擎不知道参数的确切值,但它知道应该将其视为字符串值。

这样,查询可以被解析一次(在prepare()调用期间),然后一旦解析,您可以多次执行准备好的查询,为每次执行插入不同的值( execute()调用)。 这些参数值不能改变查询的逻辑,只能改变常量值。

这就是防止 SQL 注入的好处。 SQL 注入发生在动态内容在解析之前与查询组合时,因此内容可以改变语法,从而改变查询的逻辑,使其做一些你不想要的事情。 但是如果参数在解析后组合起来,就不会影响逻辑。

在 SQL 中,表、列和模式名称不能作为参数提供给准备好的语句。 这不是 PDO 的事情,它是 SQL 本身的语言限制。

许多 RDBMS 具有准备语句一次然后使用不同数据多次使用准备好的语句的语义。 这是高效的,因为 RDBMS 软件只需制定一次查询计划(为满足查询而必须执行的操作),然后将其用于不同的数据。 如果表或列名是准备好的语句中的变量,则 RDBMS 软件无法做到这一点。 PDO 支持多个 RDBMS,因此它强制执行此语言功能。

准备好的语句也是一项安全功能。 当您提供参数并使用语句时,会仔细检查参数的数据类型。 如果表名或列​​名可以是参数,那将更加困难。

而且,这是一代人之前定义 SQL 的方式。 RDBMS 中的数据可以保存数十年。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM