我确实知道,准备好的语句是针对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注入。您可以使用简单的"白名单",正如您提到的,让变量进入您的查询。