这个函数容易受到SQL注入吗?

时间:2015-03-28 17:56:40

标签: php mysql pdo

我构建了一个函数,用于检查数据库中是否存在使用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,攻击者是否有可能破坏查询? (极端情况)

2 个答案:

答案 0 :(得分:6)

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

然而,这可能更多来自侥幸。

要正确处理用户提供的值,通常希望转义任何具有特殊含义的字符(而不是简单地删除它们)。正如Schema Object Names所述:

  

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

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

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

Permissive escaping-attempt 1

您可能想要修改您的功能以使用以下内容:

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

DON'吨!上面的代码 易受攻击(在不明显的边缘情况下)。

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是单个字符'宍';因此它将这些字符转换为代表0x8c6060的{​​{1}}。那是'宍`'引入了一个以前没有的终止反引号字符!

Permissive escaping-attempt 2

然而,可以通过使用字符集识别替换功能来解决该问题。 PHP 默认没有,但是Multibyte String等可选扩展在典型的托管环境中相当普遍。

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

Permissive escaping-attempt 3

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

白名单怎么样?

白名单通常被认为比逃避更安全可靠。但是(除非事先知道它们不包含特殊字符),仍然必须逃避列入白名单的值 - 所以这并不能帮助推进最常见的案例,尽管它确实有限制如果逃跑证明是错误的,可以造成的损害。

那么结论是什么?

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

然而,幸运的是,这不是一个常见的要求。作为一般规则,一个人的架构都应该是静态的(没有任何需要修改架构的代码库的更改)符合principle of orthogonal design:如果两者都符合在满足这些条件的情况下,SQL标识符将始终是一个静态代码的一部分,并且不需要在其位置使用用户输入。

答案 1 :(得分:3)

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

所以这很安全。

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