此函数易受SQL注入攻击吗


Is this function vulnerable to SQL injection?

我使用PDO构建了一个函数来检查数据库中是否存在表,但我不确定我是否正确地保护了它。

public function tableExists($table){
    try{
        $this->query('SELECT 1 FROM `'.str_replace('`', '', $table).'` LIMIT 1');
    }catch('PDOException $e){
        if($e->errorInfo[1] == 1146){
            return false;
        }
        throw $e;
    }
    return true;
}

如果直接从用户输入中提供$table,攻击者是否有可能破坏查询?(极端情况)

否,您的代码不易受到SQL注入攻击

然而,这可能更多是出于侥幸

为了正确处理用户提供的值,通常希望转义任何具有特殊含义的字符(而不是简单地删除它们)。如模式对象名称:下所述

如果引用标识符,则标识符中可以包含标识符引号字符。如果要包含在标识符中的字符与用于引用标识符本身的字符相同,则需要将该字符加倍。下面的语句创建一个名为a`b的表,该表包含一个名称为c"d:的列

mysql> CREATE TABLE `a``b` (`c"d` INT);

那么,我们如何允许任何表名,包括那些包含反勾字符的表名呢?

允许转义--尝试1

您可能会想修改您的函数以使用以下内容:

$this->query('SELECT 1 FROM `'.str_replace('`', '``', $table).'` LIMIT 1');

不要!上述代码易受攻击(在边缘模糊的情况下)

str_replace()是一个简单的字节式函数(它不支持字符集,因此与具有多字节字符的字符串编码一起使用是不安全的)。

如果数据库连接使用多字节字符集,例如GBK,那么恶意表名将无法正确转义:

// malicious user-provided value
$_POST['tableName'] = "'x8c`; DROP TABLE users; -- ";
// call your function with that value
tableExists($_POST['tableName']);

以上操作将导致使用以下字符串参数调用query()

SELECT 1 FROM `宍`; DROP TABLE users; -- ` LIMIT 1

这是因为当str_replace()逐字节地遍历输入的字符串时——天真地替换了'`'字符的任何出现,即字节0x60——它这样做是不理解MySQL会考虑使用GBK编码的字符串,其中0x8c60是单个字符'宍';因此它将这些字符转换成代表CCD_ 12的CCD_。这就是str_replace()引入了一个以前没有的终止反勾字符!

允许转义--尝试2

然而,这个问题可以通过使用字符集感知替换功能来解决。PHP默认情况下没有,尽管一些可选的扩展(如Multibyte String)在典型的托管环境中相当常见。

如果采用这种方法,必须确保使用数据库连接的字符编码执行替换。

允许转义--尝试3

从MySQL v.7.6开始,您可以使用mysql_real_escape_string_quote()使用数据库连接的字符集来正确地转义SQL标识符。然而,遗憾的是,PDO API没有(还?)提供这个C函数的接口…

白名单呢

白名单通常被认为比逃避更安全可靠。然而(除非事先知道它们不包含特殊字符),仍然必须转义白名单值——因此,这并不能真正帮助处理最常见的情况,尽管它确实限制了在转义被证明是错误的情况下可能造成的损害。

那么结论是什么呢

实际上,以安全的方式使用任意的、用户提供的SQL标识符是非常困难的。

然而,幸运的是,这不是一个常见的要求。一般来说,一个人的模式应该是静态的(没有任何需要修改模式的代码库更改),符合正交设计原则:如果这两个条件都满足,SQL标识符将始终是静态代码的一部分,并且不需要在其位置使用用户输入。

否。您已经删除了给定上下文中唯一有害的字符。

所以它是安全的。

至于合理,嗯。。。不。表名通常不应该来自用户输入。