如果应该准备所有 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 注入,为啥语法允许非准备查询?的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式检测基本 SQL 注入,但不能作为防止 SQL 注入的手段

PHP:使用准备好的语句并防止 SQL 注入与逃逸

防止sql注入的包

教你ASP.NET中如何防止注入攻击

如何防止sql注入攻击?

Android中是怎样防止SQL注入的?