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 注入?