7

我确实理解准备好的语句是寻求防止 SQL 注入的最终方法。但是,它们以有限的方式提供覆盖;例如,如果我让用户决定按操作的顺序(即,它是 ASC 还是 DESC?等),则准备好的语句没有覆盖。

我知道我可以将用户输入映射到一个预定义的白名单。但是,这只有在可以预先创建或彻底猜测白名单时才有可能。

例如,在我上面提到的情况下(ASC 或 DESC),这可以很容易地根据接受值列表进行映射和验证。但是不存在无法对照白名单验证部分 SQL 语句的情况吗?

如果存在这种情况,那么推荐的方法是什么?

如果我要使用底层数据库的内置转义实用程序(例如 mysql 的 mysql_real_escape_string)全面转义 user_input,我会在哪里失败?

我问这个问题的假设是我总是用引用的值构造我的 sql 语句——即使是整数......

让我们看看下面的例子并反思一下。

select {$fields} from {$table} where Age='{$age}' order by {$orderby_pref}

假设所有变量都是用户提供的。

如果我要 mysql_real_escape_string 上面 SQL 中的所有变量(而不是使用准备好的语句,它只涵盖了我的一半,迫使我为另一半提供白名单,它无济于事),它不是同样安全吗(并且更容易编码)?如果不是,在哪种输入场景中转义实用程序会失败?

$fields       = mysql_escape($fields);
$table        = mysql_escape($table);
$age          = mysql_escape($age);
$orderby_pref = mysql_escape($orderby_pref);

select {$fields} from {$table} where Age='{$age}' order by {$orderby_pref}
4

2 回答 2

3

您可以使用 PDO,您的生活会变得更轻松......:

    #   Order
    switch(strtoupper($Order)){
        default:
        case 'ASC':
            $Order = 'ASC';
            break;

        case 'DESC':
            $Order = 'DESC';
            break;
    }

    #   ID
    $ID = 39;
    $Username = 'David';

    #   Query
    $Query = $this->DB->Main->prepare('SELECT * FROM Table WHERE ID = :ID AND Username = :Username ORDER BY HellBob '.$Order);
    $Query->bindValue(':ID', $ID, PDO::PARAM_INT);
    $Query->bindValue(':Username', $Username, PDO::PARAM_STR);

    #   All good ?
    if(!$Query->execute()){
        exit('Error');
    }

    // Results
    $Row = $Query->fetch(PDO::FETCH_ASSOC);

您不必担心引号或 SQL 注入。您可以使用您提到的简单“白名单”将变量添加到查询中。

于 2012-07-12T15:10:26.450 回答
3

无论您使用准备好的语句还是mysql转义函数,您总是需要对表名或列名等内容使用白名单。

问题是表名和列名没有用单引号或双引号引起来,所以如果你使用一个专门引用这些字符的函数(当然还有更多......),它不会对你的表名做任何事情。

考虑表名my_table; DELETE * FROM mysql; SELECT * FROM my_table。此字符串中的任何内容都不会被 mysql 的转义函数转义,但它绝对是您想要检查白名单的字符串。

除此之外,mysql转义函数在字符集方面存在问题,这会使它们变得无用,因此使用准备好的语句总是更好。

于 2012-07-12T15:13:30.817 回答