如果应该准备所有 SQL 查询以防止 SQL 注入,为啥语法允许非准备查询?
Posted
技术标签:
【中文标题】如果应该准备所有 SQL 查询以防止 SQL 注入,为啥语法允许非准备查询?【英文标题】:If all SQL queries should be prepared to prevent SQL injections, why does the syntax allow non-prepared queries?如果应该准备所有 SQL 查询以防止 SQL 注入,为什么语法允许非准备查询? 【发布时间】:2020-08-20 07:38:08 【问题描述】:既然所有 SQL 查询都应该准备好以防止 SQL 注入,为什么我们允许编写和执行非准备好的查询?这看起来是不是违反直觉?
【问题讨论】:
【参考方案1】:如果查询是固定字符串并且不需要任何程序变量,则使用query()
运行它是安全的。
这是来自https://www.php.net/manual/en/pdo.query.php的示例:
<?php
$sql = 'SELECT name, color, calories FROM fruit ORDER BY name';
foreach ($conn->query($sql) as $row)
print $row['name'] . "\t";
print $row['color'] . "\t";
print $row['calories'] . "\n";
查询中没有 PHP 变量。 query()
就足够了,完成与prepare()
和execute()
相同。
如果您需要用 PHP 变量替换 SQL 表达式中的值,那么您可以使用参数:
$sql = 'SELECT name, colour, calories FROM fruit
WHERE calories < :calories AND colour = :colour';
$sth = $dbh->prepare($sql);
$sth->execute(array('calories' => 150, 'colour' => 'red'));
您可能会发现这在您的应用程序中比运行固定查询更常见。
【讨论】:
"如果您需要用 PHP 变量替换 SQL 表达式中的值,那么您将使用参数:"我认为如果您正在生成内部分配给这些变量的值。 IE。如果它是您正在计算的整数值。当然,有时使用prepare
,有时使用字符串连接会很麻烦,所以有一个论点,你最好总是使用prepare
,除非你有充分的理由不这样做。
我认为查询参数使用一致会更好,这样您就不必向初级开发人员解释如何区分何时使用它们以及何时“不必”。每当您将 PHP 变量与 SQL 查询结合使用时,只需使用查询参数作为默认参数。如果这是标准,那么您不必每次都考虑它。【参考方案2】:
即使您需要使用准备好的语句,也无法阻止通过变量替换创建准备好的语句。例如
$sql = "SELECT * FROM someTable WHERE id = $id";
$stmt = $conn->prepare($sql);
【讨论】:
实际上有一个 RFC 为数据使用“污染”属性。但结局并不好。【参考方案3】:绑定参数和query preparation 是两个不同的东西。你可以做一个或另一个或两者兼而有之。
您需要绑定参数以防止 SQL 注入。但是,有些东西不能作为参数传递(例如 ORDER BY 列表),在这种情况下,您可以将所需的 SQL 语法直接连接到 SQL 字符串中。这称为“动态 SQL”,通常应仅使用列入白名单的字符串来防止 SQL 注入。
所以回答(我认为是)您的问题:允许使用动态 SQL,因为绑定参数未涵盖某些情况。
【讨论】:
FWIW,不做prepare()
就不能使用绑定参数。
@BillKarwin 我认为这取决于客户端库。例如,ADO.NET 将允许您在不调用DbCommand.Prepare
的情况下绑定参数,或者至少它在 Oracle 和 MS SQL Server 上是这样工作的。对 ODBC 也是如此。自从我上次尝试使用 mysql 以来已经有很长时间了,如果我错了,请纠正我......
它必须将 prepare 和 execute 包装在一个方便的函数中,该函数同时执行这两个调用。但是绑定参数的目的是将动态内容与SQL解析步骤分开。如果 ADO.NET 只是将变量插入 SQL 字符串(通过一些转义使其“安全”),然后在结果字符串上调用 query()
,那么它没有执行绑定参数。
SQL Server 区分“直接”执行(将整个 SQL 文本传递给服务器)和“准备”执行(仅传递先前准备好的 SQL 语句的句柄)。完全可以在“直接”调用中包含绑定参数。没有字符串插值发生 - 这些是“真正的”绑定参数。我认为可能会出现混淆,因为术语“准备”通常被理解为简单的“解析和计划”,而忽略了“将语句句柄返回给客户端”部分。以上是关于如果应该准备所有 SQL 查询以防止 SQL 注入,为啥语法允许非准备查询?的主要内容,如果未能解决你的问题,请参考以下文章