PHP PDO:保护动态标识符免受 SQL 注入

Posted

技术标签:

【中文标题】PHP PDO:保护动态标识符免受 SQL 注入【英文标题】:PHP PDO: Protect dynamic identifiers against SQL injection 【发布时间】:2016-06-16 16:56:52 【问题描述】:

目前我正在研究一个简单工具的概念,以执行一些用 php 编写的»维护«数据库操作(删除/截断/复制表等)。

这必然要求 SQL 语句中的标识符是动态用户输入。虽然准备好的语句非常适合将 SQL 语句与任何比较值的用户输入分开,但它们并不意味着用于表或列名等标识符。 (也就是说,我can't use准备了语句来准备标识符。)

保护动态标识符的常用方法是列入白名单,但这需要静态且已知的数据库结构。例如,我想实现一个类似Copy table A and name it B 的命令。这里有趣的部分是B

假设用户已通过身份验证并允许执行此操作,我该如何保护它免受 SQL 注入?这可能吗?

我发现 an approach 建议在任何标识符中简单地引用重音符号 (`),如下所示:

$table_name        = 'origin_table'; // can be checked against existing tables
$copy_table_name   = 'user_input';
$quoted_table_name = '`' . str_replace( '`', '``', $copy_table_name ) . '`';
$sql_statement     = "CREATE TABLE $quoted_table_name LIKE $table_name";

这是否足以防止可能的 SQL 注入?

更新

PDO::quote()(在答案中提到)不是一个选项。它不会转义重音 (`):

$user_input = 'table`; TRUNCATE TABLE users --';
var_dump( $pdo->quote( $user_input ) );
//string(33) "'table`; TRUNCATE TABLE users --'"

更新 2 PostgreSQL 扩展有一个功能就是为了这个目的:https://secure.php.net/manual/en/function.pg-escape-identifier.php

【问题讨论】:

是的,但只要将完整的字符串视为标识符,mysql 就不会解释像 CHAR(96) 这样的函数。 (编辑:我回答的评论已被删除) 大声思考:我会让用户在表单中输入表名和列名。现在,这些可以验证无意义的字符和代码。然后可以轻松地检查它们是否与mysql data dictionary 匹配。任何不匹配都会出错。我也会有一个表和/或列的“白名单”。从提供的信息生成所需的 SQL 是一项相当简单的任务。 IE。想到query builders @RyanVincent 谢谢,我会处理这些问题。最佳解决方案是使用像TableCopier::copyTables( $src, $dest ) 这样的方法。我当然可以对此方法写评论:«不要将未过滤的值传递给它!»。但这并不能保护它免于被使用并在野外造成 SQL 漏洞。 我误会了。表单的输入必须是有效的表名和列名,否则它们将与mysql data dictionary不匹配? 【参考方案1】:

正如您提到的 PDO 的 quote() 函数可用于转义列名和数据。或者使用准备好的语句,例如 CREATE TABLE ?喜欢 ?然后执行查询。

【讨论】:

PDO 准备好的语句不可行because:»参数标记只能表示完整的数据文字。«(但不是标识符。)quote() 方法需要去除第一个和最后一个 @字符串中的 987654323@ 向我表明我使用它的方式不是它应该被使用的方式。我在问题中链接的评论还指出,转义标识符和转义 »strings« 存在差异。【参考方案2】:

您总是希望转义 ` 标识符,甚至是表名和字段名,因为也许明天它们将成为保留字并破坏您的查询。只要您使用准备好的语句(PDO、mysqli 或其他),就可以了。请注意,PDO 不允许转义表或字段名称。

您只需要细化您的过滤机制,例如允许和不允许的表名等。

顺便说一句:不要像你想做的那样做:“CREATE TABLE $quoted_table_name LIKE $table_name”;.. 用户准备的带有占位符的语句(?,或:名称等)

编辑: 由于您需要保护标识符,我看到了两种方法:

    用 (`) 将它们括起来 过滤和转义传入值(mysql_real_escape_string 或更好)。过滤将包括去除任何不必要的字符(您可能只想允许字母 [a-z]+ 或带数字的字母。

【讨论】:

PDO 准备好的语句不可行because:»参数标记只能表示完整的数据文字。«(但不是标识符。) 你转义然后在常规 mysql 中,PDO 负责参数和查询分离。但回到您的问题,简单地说,您正在执行的查询是什么?您不只是将表名作为输入并执行查询吗?我看不出问题出在哪里? 查询实际上是不相关的。问题是,如何在 SQL 查询中安全地使用用户输入的标识符(表名、列名)。

以上是关于PHP PDO:保护动态标识符免受 SQL 注入的主要内容,如果未能解决你的问题,请参考以下文章

在 PHP 中使用准备好的语句/存储过程时如何保护自己免受 SQL 注入?

使用 PDO 准备参数化查询

我是不是足够保护我的网站免受 sql 注入?

防止 SQL 注入的 Htaccess 和 Mysqli 方法之间的区别

如何保护此代码免受 SQL 注入?有点迷茫

如何保护加载数据本地infile更新查询免受sql注入