PHP:使用准备好的语句,防止SQL注入与转义


PHP: using prepared statements and protecting against SQL injection vs escape

我确实知道,准备好的语句是针对SQL注入寻求保护的最终方法。然而,它们提供的覆盖范围有限;例如,在我让用户决定按操作排序的情况下(即,是ASC还是DESC?等),我没有准备好的语句。

我知道我可以将用户输入映射到预定义的白名单中。但是,只有在事先可以创建或完全猜测白名单的情况下,这才有可能。

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

如果存在这种情况,那么建议采取什么方法?

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

我问这个问题的前提是,我总是用引号来构造sql语句——即使是整数。。。

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

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

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

如果我使用mysql_real_eescape_string上面SQL中的所有变量(而不是使用只覆盖我一半的准备好的语句,迫使我为另一半列出白名单,这对我没有帮助),它不是同样安全(更容易编码)吗?如果没有,在哪个输入场景中escape实用程序会失败?

$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}

无论使用prepared语句还是mysql转义函数,对于表名或列名等内容,都需要使用白名单

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

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

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

你可以使用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注入。您可以使用简单的"白名单",正如您提到的,让变量进入您的查询。