在“ORDER BY”条款之后可以提出任何可能造成安全风险的事情吗?

时间:2011-07-11 18:01:33

标签: php mysql database sql-injection

基本上,我想要做的是:

mysql_query("SELECT ... FROM ... ORDER BY $_GET[order]")

他们显然可以通过在其中添加无意义来轻松地创建SQL错误,但mysql_query仅允许您执行1个查询,因此他们不能放置1; DROP TABLE ...之类的内容。

除了创建语法错误外,恶意用户是否可以造成任何损害?

如果是,我该如何清理查询?

$_GET['order']变量基于类似SQL的语法构建了很多逻辑,所以我真的不想改变格式。


为了澄清,$_GET['order']不仅仅是一个字段/列。它可能类似于last_name DESC, first_name ASC

5 个答案:

答案 0 :(得分:30)

是的,SQL注入攻击可以使用未转义的ORDER BY子句作为向量。这里有一个解释如何利用它以及如何避免这个问题:

http://josephkeeler.com/2009/05/php-security-sql-injection-in-order-by/

该博客文章建议使用白名单来验证ORDER BY参数,这几乎肯定是最安全的方法。


要响应更新,即使该子句很复杂,您仍然可以编写一个针对白名单验证它的例程,例如:

function validate_order_by($order_by_parameter) {
    $columns = array('first_name', 'last_name', 'zip', 'created_at');

    $parts = preg_split("/[\s,]+/", $order_by_parameter);

    foreach ($parts as $part) {
        $subparts = preg_split("/\s+/", $part);

        if (count($subparts) < 0 || count($subparts) > 2) {
           // Too many or too few parts.
           return false;
        }

        if (!in_array($subparts[0], $columns)) {
           // Column name is invalid.
           return false;
        }

        if (count($subparts) == 2 
            && !in_array(strtoupper($subparts[1]), array('ASC', 'DESC')) {
          // ASC or DESC is invalid
          return false;
        }
    }

    return true;
}

即使ORDER BY子句很复杂,它仍然仅使用您提供的值(假设您不允许用户手动编辑它)。您仍然可以使用白名单进行验证。

我还应该补充一点,我通常不喜欢在URL或UI中的其他位置公开我的数据库结构,并且通常会在URL中的参数中对这些内容进行别名,并使用哈希将其映射到实际值。

答案 1 :(得分:17)

不要指望当时的SQL注入不会导致问题;不允许任何SQL注入。如果不出意外,恶意攻击者可以定义一个非常复杂的订单,这可能会导致数据库严重减速。

答案 2 :(得分:6)

我更喜欢进行白名单,并将http参数字符串视为与插入SQL查询的字符串分开。

在下面的PHP示例中,数组键将是作为http参数传递的值,根据您的Web界面,是不同排序方案的某种符号标签。对于这些相应的排序方案,数组值将是我们想要插入SQL的内容,例如,列名或表达式。

<?php

$orderby_whitelist = array(
  "name"    => "last_name, first_name",
  "date"    => "date_created",
  "daterev" => "date_created DESC",
  "DEFAULT" => "id"
);

$order = isset($_GET["order"]) 
  ? $_GET["order"] 
  : "DEFAULT";
$order_expr = array_key_exists($order, $orderby_whitelist) 
  ? $orderby_whitelist[$order] 
  : $orderby_whitelist["DEFAULT"];

mysql_query("SELECT ... FROM ... ORDER BY $order_expr")

这有以下优点:

  • 即使您无法使用查询参数,也可以防御SQL注入。如果客户端传递了无法识别的值,则代码会忽略它并使用默认顺序。

  • 您不必清理任何内容,因为数组中的键和值都是由程序员编写的。客户输入只能选择您允许的其中一个选项。

  • 您的网络界面未显示您的数据库结构。

  • 您可以制作与SQL表达式或替代ASC / DESC相对应的自定义订单,如上所示。

  • 您可以在不更改Web界面的情况下更改数据库结构,反之亦然。

我在演示文稿中SQL Injection Myths & Fallacies以及我的书SQL Antipatterns: Avoiding the Pitfalls of Database Programming中介绍了此解决方案。

答案 3 :(得分:2)

围绕变量$_GET['order']构建的逻辑在变量$order_sanitized上使用时应该同样有效。为什么不首先清理输入,然后执行逻辑以使其适合SELECT语句?

答案 4 :(得分:-1)

它仍然是潜在的SQL注入点。您取决于mysql_query的内部实施。如果在更高版本中他们将其更改为使用多个查询,该怎么办?

您可以使用mysql_real_escape_string