【问题标题】:Is Double escaping a String wrong?Double 转义字符串是错误的吗?
【发布时间】:2010-11-13 06:33:29
【问题描述】:

我有一个数据库类,它在使用 mysqli_real_escape_string() 构建查询之前自动转义输入字符串。 但可能是在脚本中,一个字符串被转义,然后传递给再次转义它的数据库类。那可能是错的吗?会发生什么?

【问题讨论】:

  • 回答你在 cmets 中问了 5 次的问题......想象一下,如果你有一个表单输入“O'Brien”。现在,如果这被转义了两次,那么您将获得 SQL 查询“... VALUES('O\\\'Brien') ...”。这会导致“O\'Brien”存储在您的数据库列中。如果您随后检索它并且不取消转义,您将向用户显示 O\'Brien,尽管他们想要 O'Brien 的值。

标签: php mysql string escaping


【解决方案1】:

由于你没有很好的方法告诉 other 端(当你从数据库中提取数据时)它被转义了多少次,不一致地双重转义字符串会缠绕随着您在最终数据中转义了以前没有的东西 - 因为您将添加两层转义但只取消转义一层。

基本上,您需要有一个恒定的级别转义 - 要么总是转义一次(并取消转义一次),要么总是转义两次(并取消转义两次)等等 - 但永远不要混合。

【讨论】:

  • 如何转义?假设它是否被逃脱了两次?
【解决方案2】:

在第一次通过mysqli_real_escape_string 时,通过在每个危险字符前插入\ 来转义以下字符:

NUL (ASCII 0)、\n、\r、\、'、" 和 Control-Z:

NUL (chr(0)) becomes "\0" (chr(92).chr(48))
\n (chr(13)) becomes "\n" (chr(92).chr(110))
\r (chr(10)) becomes "\r" (chr(92).chr(114))
\ (chr(92)) becomes "\\" (chr(92).chr(92))
' (chr(39)) becomes "\'" (chr(92).chr(39))
" (chr(34)) becomes "\"" (chr(92).chr(34))
Control-Z (chr(26)) becomes "\Z" (chr(92).chr(90))

在第二次通过mysqli_real_escape_string 时,\ 再次被转义:

"\0" (chr(92).chr(48)) becomes "\\0" (chr(92).chr(92).chr(48))
"\n" (chr(92).chr(110)) becomes "\\n" (chr(92).chr(92).chr(110))
"\r" (chr(92).chr(114)) becomes "\\r" (chr(92).chr(92).chr(114))
"\\" (chr(92).chr(92)) becomes "\\\\" (chr(92).chr(92).chr(92).chr(92))
"\'" (chr(92).chr(39)) becomes "\\'" (chr(92).chr(92).chr(39))
"\"" (chr(92).chr(34)) becomes "\\"" (chr(92).chr(92).chr(34))
"\Z" (chr(92).chr(90)) becomes "\\Z" (chr(92).chr(92).chr(90))

对字符串进行双重转义不会造成任何类型的漏洞,但它会在您保存到数据库中的字符串中插入许多额外的“\”字符。

进行转义的最佳方法是: 1)关闭魔术引号 2) 仅使用带有命名参数的查询,并且在将其传递给查询之前不要转义任何内容。 MySQL(以及所有其他数据库供应商)将正确地转义字符串。 (但是,您可能会遇到 chr(0) 终止字符串的问题)。

如果您绝对必须使用字符串查询,请在将数据插入查询之前将数据转义一次,并且只转义一次。不要转义整个查询。

【讨论】:

  • 没有所谓的“危险人物”。 SQL 中有一些具有特殊含义的字符。你必须转义它们以指定它们的字面意思。
【解决方案3】:

只要保证在使用前对字符串进行双重转义,对字符串进行双重转义就没有错。

如果你忘记对它进行双重转义,那么用户最终会读取一个转义的字符串,这不是很好。

【讨论】:

  • 我怎样才能取消它?我为什么要这样做?
  • 如果你使用 mysqli_real_escape_string() 转义一次,那么 MySQL 库将为你取消转义,你不必担心,但如果你的脚本完成了一些额外的转义,那么它将是您的脚本有责任避免它。据我所知,没有用于取消转义的标准 MySQL 库函数,因此正确执行此操作可能会很棘手。 stripslashes() 可能有用,但不知道它会破坏什么。
  • +1 用于提及双重转义。 @Charlie Pigarelli:您可能会注意到,如果您在我们的查询中双重转义 quoted string,您最终会得到类似:(SELECT * FROM xxx WHERE yyy = 'zzz zzz') 变为 (SELECT * FROM xxx WHERE yyy = \'zzz zzz\') escaped 并成为附加的`\`
【解决方案4】:

除非您考虑到这一点,否则双重转义的字符串将无法正确呈现。您可能已经看到偶尔以转义形式呈现文本的示例(例如,带有额外的反斜杠或 HTML 实体)。

【讨论】:

    【解决方案5】:

    是的,这可能是错误的,因为您的数据库中可能存在 let\'s go 之类的内容。

    您转义是为了避免 SQL 注入 或查询语法错误,而不是“转换”您要存储的数据。如果您想将 let's go 存储在数据库中,您应该在构建查询时只转义一次。您不必“取消转义”来自数据库的值。当您查看数据库时,您永远不会看到带有转义字符的值。

    转义对于 PHP 初学者来说是一个真正的问题,这可能是由于 magic_quote() 函数。你应该看看http://php.net/manual/en/security.magicquotes.php 你不应该使用那个功能,它很混乱。在您的脚本中,如果您无法修改 php.ini 以禁用此功能,您可以在运行时使用:

    if (get_magic_quotes_gpc()) {
    $process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
    while (list($key, $val) = each($process)) {
        foreach ($val as $k => $v) {
            unset($process[$key][$k]);
            if (is_array($v)) {
                $process[$key][stripslashes($k)] = $v;
                $process[] = &$process[$key][stripslashes($k)];
            } else {
                $process[$key][stripslashes($k)] = stripslashes($v);
            }
        }
    }
    unset($process);
    }
    

    注意此代码来自http://www.php.net/manual/en/security.magicquotes.disabling.php

    花点时间正确理解如何处理转义,将来会节省大量时间。

    【讨论】:

      【解决方案6】:

      对于 MySQL,使用准备好的语句,您无需在数据库级别转义字符串。

      对于 PHP,请记住您可以同时使用 " 和 ' 来构建字符串。您可以使用它来避免必须引用字符串。如果字符串以 " 开头,那么您不必引用您的 '。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-14
        • 1970-01-01
        • 2014-09-20
        • 1970-01-01
        • 2012-02-12
        • 1970-01-01
        • 2018-07-13
        • 2011-06-30
        相关资源
        最近更新 更多