PHP:使用预准备语句并防止SQL注入vs转义

时间:2012-07-12 15:05:12

标签: php prepared-statement sql-injection mysql-real-escape-string

我确实理解准备好的语句是寻求防止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}

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

如果我在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}

2 个答案:

答案 0 :(得分: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注入。您可以使用简单的“白名单”来提升您的查询。

答案 1 :(得分:3)

无论是使用预准备语句还是mysql转义函数,您总是需要使用白名单来表示表名或列名

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

考虑表名my_table; DELETE * FROM mysql; SELECT * FROM my_table。这个字符串中的任何内容都不会被mysql的转义函数转义,但它肯定是一个你想要检查白名单的字符串。

除此之外,mysql转义函数的字符集存在问题,导致它们无效,因此使用预处理语句总是更好。