【问题标题】:Escaping column names in PDO statements在 PDO 语句中转义列名
【发布时间】:2010-12-05 06:50:06
【问题描述】:

我目前正在构建一个查询,其中字段/列和值部分都可能包含用户输入的数据。

问题在于转义字段名。 我正在使用准备好的语句来正确转义和引用值,但是在转义字段名时我遇到了麻烦。

  • mysql_real_escape_string 需要一个 mysql 连接资源才能给我们,所以排除了
  • PDO::quote 在字段名周围添加引号,这使得它们在查询中也无用
  • addslashes 有效,但并不安全

任何人都知道在将字段名传递给 PDO::prepare 之前将其正确插入查询的最佳方法是什么?

【问题讨论】:

    标签: php mysql pdo


    【解决方案1】:

    进行分隔标识符的 ANSI 标准方法是:

    SELECT "field1" ...
    

    如果名称中有 ",则加倍:

    SELECT "some""thing" ...
    

    不幸的是,这在 MySQL 的默认设置中不起作用,因为 MySQL 更喜欢认为双引号是字符串文字的单引号的替代方案。在这种情况下,您必须使用反引号(如 Björn 所述)和反斜杠转义。

    要正确进行反斜杠转义,您需要 mysql_real_escape_string,因为它依赖于字符集。但这一点没有实际意义,因为mysql_real_escape_string 和addslashes 都不会转义反引号字符。如果您可以确定列名中永远不会有非 ASCII 字符,则只需手动反斜杠转义 ` 和 \ 字符即可。

    无论如何,这与其他数据库不兼容。您可以通过设置配置选项 ANSI_QUOTES 来告诉 MySQL 允许使用 ANSI 语法。同样,SQL Server 默认情况下也会阻塞双引号;它使用另一种语法,即方括号。同样,您可以使用“quoted_identifier”选项将其配置为支持 ANSI 语法。

    总结:如果只需要 MySQL 兼容性:

    一个。使用反引号并禁止名称中的反引号、反斜杠和 nul 字符,因为转义它们是不可靠的

    如果您需要跨 DBMS 兼容性,则:

    b.使用双引号并要求 MySQL/SQL-Server 用户适当地更改配置。不允许在名称中使用双引号字符(因为 Oracle 甚至无法处理它们甚至转义)。或者,

    c。有一个 MySQL vs SQL Server vs Others 的设置,并根据它生成反引号、方括号或双引号语法。禁止使用双引号和反斜杠/反引号/nul。

    这是您希望数据访问层有一个功能,但 PDO 没有。

    总结总结:任意列名是个问题,如果你能提供帮助,最好避免。

    总结总结:gnnnnnnnnnnnh。

    【讨论】:

    • 感谢您澄清白痴的情况。我认为这里的成人方法可能是在列名不匹配 [a-z0-9_] 时简单地抛出异常,而不是尝试围绕所有可能的漏洞进行编码。
    • @Maryam Jeddian 的回答怎么样? str_replace("`", "``", $fieldname) 看起来很合理。在反引号内,您不需要转义斜线。我只是不确定空字符。对这个答案有什么想法吗?
    • @Kamil:嗯...我可以发誓,当我上次测试它时,反斜杠确实在反引号中具有转义效果。仍然有一些奇怪的事情发生 - 尝试输入 SELECT `a\``b` FROM sometable; MySQL 变得很困惑。所以我会坚持“根本不允许字符”。
    【解决方案2】:

    正确答案是

    str_replace("`", "``", $fieldname)

    错误:

    mysql> 从用户中选择`col\"umn`; 错误 1054 (42S22):“字段列表”中的未知列 'col\"umn'

    对:

    mysql> 从用户中选择`kid`s`; 错误 1054 (42S22):“字段列表”中的未知列“孩子” mysql> SELECT ```column``name``` FROM user; 错误 1054 (42S22):“字段列表”中的未知列“列名”

    (请注意,在上一个示例中,列名中有 3(三)个额外的反引号,只是为了显示一种极端情况)

    【讨论】:

      【解决方案3】:

      这可能会影响性能,但应该是安全的。

      首先运行 DESCRIBE 表查询以获取允许的字段名称列表,然后将这些与用户提交的数据进行匹配。

      如果匹配,那么您可以使用用户提交的数据而无需任何转义。

      如果不匹配,则为拼写错误或 hack - 无论哪种方式,它都是输入数据中的“错误”,不应运行查询。

      通过运行 SHOW TABLES 查询并从该结果集中进行匹配,可以对“动态”表名进行同样的操作。

      在我的一个应用程序中,我有一个“安装”脚本;其中一部分查询数据库和表字段名称,然后编写一个始终被引用的 php 文件,因此我不会经常对数据库运行 DESCRIBE 查询,例如

      $db_allowed_names['tableName1']['id'] = 1;
      $db_allowed_names['tableName1']['field1'] = 1;
      $db_allowed_names['tableName1']['field2'] = 1;
      $db_allowed_names['tableName2']['id'] = 1;
      $db_allowed_names['tableName2']['field1'] = 1;
      $db_allowed_names['tableName2']['field2'] = 1;
      $db_allowed_names['tableName2']['field3'] = 1;
      
      if($db_allowed_names['tableName1'][$_POST['field']]) {
           //ok
      }
      

      我使用这样的数组键,因为 if 语句比 in_array 查找快一点

      【讨论】:

        【解决方案4】:

        这样的事情怎么样?

        function filter_identifier($str, $extra='') {
            return preg_replace('/[^a-zA-Z0-9_'.$extra.']/', '', $str);
        }
        
        try {
            $res = $db->query('SELECT '.filter_identifier($_GET['column'], '\*').' FROM '.filter_identifier($_GET['table']).' WHERE id = ?', $id);
        } catch (PDOException $e) {
            die('error querying database');
        }
        

        这是一个简单的基于白名单的字符列表。任何不在列表中的字符都将被删除。对我来说幸运的是,我能够制作数据库和表格,所以我知道“a-zA-Z0-9_”之外永远不会有任何字符(注意:没有空格)。您可以通过 $extra 参数向列表中添加额外的字符。如果有人尝试将 "(SELECT * FROM users);--" 放在“列”中,它会过滤到 "SELECT*FROMusers",这将抛出异常:)

        如果可能的话,我会尽量避免进行额外的查询(我对性能非常敏感)。所以像事先做一个 DESCRIBE 或者硬编码一组表/列来检查是我不想做的事情。

        【讨论】:

          【解决方案5】:

          一个奇怪的项目设计,但对于您的问题:用 ` 将您的字段名称括起来,并使用加斜线作为名称。

          select `field1`, `field2` from table where `field3`=:value
          

          【讨论】:

          • 这确实是解决方案之一,但我一直在思考,并且出现了一些更简单的东西......我只是在描述表格并从数组中过滤任何具有键的条目与我表中的列无关...这样我绝对确定只使用现有列,否则我们会在 em 处引发异常
          • 这不起作用,因为用户仍然可以通过提供包含反引号的字段名来打破反引号
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-01-19
          • 1970-01-01
          • 2012-11-07
          • 2013-05-28
          • 2013-08-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多