我构建了一个函数,用于检查数据库中是否存在使用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
,攻击者是否有可能破坏查询? (极端情况)
答案 0 :(得分:6)
然而,这可能更多来自侥幸。
要正确处理用户提供的值,通常希望转义任何具有特殊含义的字符(而不是简单地删除它们)。正如Schema Object Names所述:
如果引用标识符,标识符引号字符可以包含在标识符中。如果要包含在标识符中的字符与用于引用标识符本身的字符相同,则需要将该字符加倍。以下语句创建一个名为
a`b
的表,其中包含名为c"d
的列:mysql> CREATE TABLE `a``b` (`c"d` INT);
那么我们如何允许任何表名,包括那些包含反引号字符的表名?
您可能想要修改您的功能以使用以下内容:
$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
是单个字符'宍'
;因此它将这些字符转换为代表0x8c6060
的{{1}}。那是'宍`'
引入了一个以前没有的终止反引号字符!
然而,可以通过使用字符集识别替换功能来解决该问题。 PHP 默认没有,但是Multibyte String等可选扩展在典型的托管环境中相当普遍。
如果遵循此方法,必须确保使用数据库连接的字符编码执行替换。
从MySQL v5.7.6开始,您可以使用mysql_real_escape_string_quote()
使用数据库连接的字符集正确转义SQL标识符。然而,遗憾的是,PDO API(还没有)提供了这个C函数的接口......
白名单通常被认为比逃避更安全可靠。但是(除非事先知道它们不包含特殊字符),仍然必须逃避列入白名单的值 - 所以这并不能帮助推进最常见的案例,尽管它确实有限制如果逃跑证明是错误的,可以造成的损害。
以安全的方式使用任意的,用户提供的SQL标识符实际上非常困难。
然而,幸运的是,这不是一个常见的要求。作为一般规则,一个人的架构都应该是静态的(没有任何需要修改架构的代码库的更改)和符合principle of orthogonal design:如果两者都符合在满足这些条件的情况下,SQL标识符将始终是一个静态代码的一部分,并且不需要在其位置使用用户输入。
答案 1 :(得分:3)
没有。您已删除了给定上下文中唯一有害的字符。
所以这很安全。
至于合理,嗯......没有。表名通常不应该来自用户输入。