如何防止使用动态表名进行 SQL 注入?

Posted

技术标签:

【中文标题】如何防止使用动态表名进行 SQL 注入?【英文标题】:How can I prevent SQL injection with dynamic tablenames? 【发布时间】:2011-08-14 06:52:41 【问题描述】:

我以很高的声誉php家伙进行了这个讨论:

PDO 在这里没有用。以及 mysql_real_escape_string。 质量极差。

这当然很酷,但我真的不知道建议使用 mysql_real_escape_string 或 PDO 来修复此代码有什么问题:

<script type="text/javascript">
    var layer;

    window.location.href = "example3.php?layer="+ layer;

    <?php
        //Make a MySQL connection
        $query = "SELECT Category, COUNT(BUSNAME)
          FROM ".$_GET['layer']." GROUP BY Category";
        $result = mysql_query($query) or die(mysql_error());

进入这个

$layer = mysql_real_escape_string($_GET['layer']);
$query = "SELECT Category, COUNT(BUSNAME)
FROM `".$layer."` GROUP BY Category";

,考虑到JavaScript 代码被发送到客户端。

【问题讨论】:

有人可以发布示例代码如何修复这个 SQL 注入漏洞吗? @nikic 我知道你要去哪里,但它看起来并不万无一失:-) 是的,我也不认为它是万无一失的。我看到的问题是与编码相关的东西,正如我在下面的回答中提到的那样。但我不知道这些基于编码的黑客是如何工作的,因此不知道如何防止它们。 【参考方案1】:

为了记录,这里是修复这个洞的示例代码。

$allowed_tables = array('table1', 'table2');
$clas = $_POST['clas'];
if (in_array($clas, $allowed_tables)) 
    $query = "SELECT * FROM `$clas`";

【讨论】:

解释一下。【参考方案2】:

为了回答如何实际修复代码:

'...FROM `' . str_replace('`', '``', $tableName) . '`...'

这会复制表名中的所有反引号(这就是 MySQL 中的转义方式)。

我不确定的一件事是,这是否是“编码安全的”(如何正确称呼它?)。通常推荐mysql_real_escape_string 而不是addslashes,因为前者考虑了MySQL 连接的编码。也许这个问题在这里也适用。

【讨论】:

@nikic,这不会阻止使用table`` union select x,y from mysql.user 作为表名 @Johan:为什么不呢?它将插入为:... FROM `table```` union select x,y from mysql.user` ... 所以省略反引号并注入table&amp;#96; union select x,y from mysql.user @Johan:&amp;#96; 应该是什么意思?如果这是一个等同于反引号的 html 实体,则: HTML 实体仅在 HTML 中工作,而不是在 SQL 中。在 SQL 中,这些只是普通字符。 @nikic,我知道您想假装您的代码是安全的,但相信我不是,这取决于您的数据库连接元字符的字符集/排序规则,例如上面的元字符将被翻译成特殊的反引号之类的字符。唯一安全的方法是对照预先批准的名称列表检查表名/字段名/列名。如果你不相信我,问@Pekka。【参考方案3】:

你的建议确实不正确。

mysql_real_escape_string() 不适用于动态表名;它旨在转义字符串数据,仅由引号分隔。它不会逃脱反引号字符。这是一个很小但至关重要的区别。

所以我可以在其中插入 SQL 注入,我只需要使用结束反引号。

PDO does not provide sanitation for dynamic table names, either。

这就是为什么最好不要使用动态表名,或者如果必须使用,将它们与有效值列表进行比较,例如来自SHOW TABLES 命令的表列表。

我也没有真正完全意识到这一点,并且可能因为重复同样的错误建议而感到内疚,直到在 SO 上向我指出了这一点,也是由 Shrapnel 上校指出的。

【讨论】:

+1 很好的答案。我今天肯定学到了一些新东西! +1 这是我在 PDO 中长期错过的东西。 PDO-&gt;quote(..., PDO::PARAM_IDENTIFIER) 或类似的东西。这通常会削弱 PDO 跨数据库方法,因为不同的数据库对此有不同的引用和转义。在 MySQL 中,您最常使用反引号," 只是ANSI 模式下的标识符引用,但另一方面,在其他 DBMS 中," 是要走的路。哼哼哼哼:) @nikic 是的!这绝对是PDO的一个缺点。我也不明白。 SHOW TABLES +1...您可以缓存结果以避免每次都调用它。

以上是关于如何防止使用动态表名进行 SQL 注入?的主要内容,如果未能解决你的问题,请参考以下文章

语句有动态表名时如何防止SQL注入?

怎样防止sql注入

正确插入表名

mybatis在传参时,为啥#能够有效的防止sql注入

如何在 SQL Server 中清理(防止 SQL 注入)动态 SQL?

SQL Server如何防止动态sql中的sql注入