【问题标题】:Escaping user input from database necessary?需要从数据库中转义用户输入吗?
【发布时间】:2011-11-18 11:49:56
【问题描述】:

所以我知道 MySQL 注入,并且总是在将所有用户输入放入我的数据库之前对其进行转义。但是我想知道,想象一个用户试图提交一个要注入的查询,而我却逃避了它。如果我稍后从数据库中获取这个值,并在查询中使用它会怎样。我必须再次逃脱吗?

所以:(sql::escape() 包含我的转义函数)

$userinput = "'); DROP `table` --";
mysql_query("INSERT INTO `table` 
             (`foo`,`bar`) 
             VALUES 
             ('foobar','".sql::escape($userinput)."')");

// insert php/mysql to fetch `table`.`bar` into $output here

mysql_query("INSERT INTO `table2` 
            (`foo`,`bar`) 
            VALUES
            ('foobar','".$output."')");

MySQL 是否会自动转义它们的输出或类似的东西,还是我也应该在第二个查询中转义?

这是一个测试用例,但这在我的程序中以其他方式发生,我想知道这种情况的安全性必须有多严格。

编辑

我的逃生函数

static function escape($string){

    if(get_magic_quotes_gpc()) 
        $string = stripslashes($string); 

    return mysql_real_escape_string($string);

}

【问题讨论】:

标签: php mysql escaping sql-injection user-input


【解决方案1】:

MySQL 是否会自动转义它们的输出或类似的东西,还是我也应该在第二个查询中转义?

您还需要在第二个查询中转义。 MySQL 不会对其输出进行任何转义。

长答案:MySQL 字符串转义不会修改正在插入的字符串,它只是确保它不会对当前查询造成任何伤害。任何 SQL 注入尝试仍保留在数据中。

【讨论】:

  • 我想这是一个简单的答案,但这是一个简单的问题。干杯:)
  • @Kokos,您还需要注意magic_quotes_gpc!如果没有关闭,您将遇到问题。请参阅我的帖子以获得解释。
【解决方案2】:

如果可以,请尝试使用 PHP 的 PDO 进行数据库访问。这有两个重要原因:

  1. 您可以使用 PDO 的 prepare 函数来编译您的查询。如果您需要使用不同的输入发出相同的查询(通常是这种情况),这将非常有效。所以,编译一次,执行多次。
  2. 使用prepare 编译查询还有其他很好的效果。一旦查询被编译,数据库引擎就知道查询的确切句法结构,并且不允许任何改变这个句法结构的输入。这很好,因为在 SQL 注入中,注入的输入会改变查询的语法。

警告:这并不能阻止所有类型的 SQL 注入,但可以阻止最常见的类型。

参考资料:

  1. Are PDO prepared statements sufficient to prevent SQL injection?
  2. http://php.net/manual/en/pdo.prepare.php

【讨论】:

    【解决方案3】:

    是的,您也必须在第二个查询中转义字符串。

    逃出绳子对很多人来说听起来很神奇,就像屏蔽某种神秘的危险一样,但实际上这没什么神奇的。这只是启用查询处理特殊字符的方法。

    最好只是看看转义的真正作用。假设输入字符串是:

    '); DROP `table` --
    

    转义后:

    \'); DROP `table` --
    

    实际上它只转义了一个斜线。这是您唯一需要保证的——当您在查询中插入字符串时,语法会正常!

    insert into table set column = '\'); DROP `table` --'
    

    它不像危险盾什么的神奇,只是为了确保结果查询具有正确的语法!(当然如果没有,它可以被利用)

    查询解析器然后查看 \' 序列并知道它仍然是变量,而不是其值的结尾。它将删除反斜杠,并将以下内容存储在数据库中:

    '); DROP `table` --
    

    这与用户输入的值完全相同。这正是您想要在数据库中拥有的!

    所以这意味着如果您从数据库中获取该字符串并想再次在查询中使用它,您需要再次转义它以确保生成的查询具有正确的语法 .

    但是,在您的示例中,要提到的非常重要的一点是 magic_quotes_gpc 指令!

    此功能会自动转义所有用户输入(gpc - _GET、_POST 和 _COOKIE)。 这是为不了解 sql 注入的人设计的邪恶功能。它是邪恶的有两个原因。第一个原因是你必须区分你的第一个和第二个查询的情况——第一个你不逃跑,第二个你逃跑。大多数人所做的要么是关闭“功能”(我更喜欢这个解决方案),要么首先取消转义用户输入,然后在需要时再次转义。转义代码可能如下所示:

    function stripslashes_deep($value)
    {
            return is_array($value) ?
                   array_map('stripslashes_deep', $value) :
                   stripslashes($value);
    }
    
    if (get_magic_quotes_gpc()) {
            $_POST = stripslashes_deep($_POST);
            $_GET = stripslashes_deep($_GET);
            $_COOKIE = stripslashes_deep($_COOKIE);
    }
    

    这是邪恶的第二个原因是因为没有什么比得上“万能引用”。 引用时,您总是为某些特定输出引用文本,例如:

    1. mysql查询的字符串值
    2. like mysql 查询表达式
    3. html代码
    4. json
    5. mysql 正则表达式
    6. php 正则表达式

    对于每种情况,您需要不同的引用,因为每种用法都存在于不同的语法上下文中。这也意味着 不应在 PHP 的输入中进行引用,而应在特定的输出中进行引用!这就是为什么像magic_quotes_gpc 这样的功能被破坏的原因(永远不要忘记处理它,或者更好的是,确保它被关闭!!!)。

    那么,在这些特殊情况下,可以使用哪些方法进行引用? (请随时纠正我,可能有更现代的方法,但这些对我有用)

    1. mysql_real_escape_string($str)
    2. mysql_real_escape_string(addcslashes($str, "%_"))
    3. htmlspecialchars($str)
    4. json_encode() - 仅适用于 utf8!我将我的函数用于 iso-8859-2
    5. mysql_real_escape_string(addcslashes($str, '^.[]$()|*+?{}')) - 在这种情况下你不能使用 preg_quote 因为反斜杠会被转义两次!
    6. preg_quote()

    【讨论】:

    • 我刚刚确保我的整个 sql 课程不会运行,除非 magic_quotes 被关闭。
    • @Kokos,这是不正确的,因为在您的第二个查询中您将无法逃脱,这是错误的!!!最好确保 magic_quotes_gpc 已关闭,或者如果您无法将其关闭,请立即取消转义用户输入,如上所示。
    • 不是那样的,我不会像我在回答中那样运行查询,这只是一个例子。我的sql 类是与数据库交互的每个页面的依赖项,所以我想这应该可以解决问题:if(get_magic_quotes_gpc()) die('Turn magic quotes off before proceeding.'); 对吗?除非将其关闭,否则该网站将无法加载。
    • @Kokos,啊哈,那我认为没关系(前提是您为每个参数化的 sql 查询运行 sql 类)。
    • 除其他外,该类用于连接到数据库(在此之前我现在检查魔术引号),因此这意味着没有魔术引号关闭的连接。 :)
    【解决方案4】:

    我会说这个问题的整个想法是错误的。

    您完全错误地解决了这个问题。
    一个人不必计算他的查询,如果它是第一个、第二个或第 100 个。
    用户输入也是如此:数据来自哪里并不重要!

    数据目的地,而不是来源应该是您的关注点。这个字符串会进入数据库吗?躲开它!毫无疑问。这条规则简单明了,不需要查询计数或任何东西。

    但这不仅仅是你的问题的错。
    一个:

    MySQL 会自动转义它们的输出或类似的东西吗?

    这是一个非常糟糕的想法。有趣的是,通过应用 get_magic_quotes_gpc(),您正在与代码中相同想法的结果作斗争。如果不是这种自动转义,这些魔术引号是什么?

    两个:
    此外,在转义函数中使用 get_magic_quotes_gpc() 又是一个非常糟糕的主意:)

    想象你有魔术引号并使用你的函数来保护你的“第二个查询”。并且数据中有一些包含\' 序列的blob。您的函数将去除斜线并破坏数据。实际上,stripslashes 与任何转义函数完全无关。在它所属的数据上单独进行 - 在用户输入上。

    三:
    mysql_real_escape_string() 不是“使一切安全”的神奇功能。实际上,要创建动态 mysql 查询,必须转义 四种 种数据:

    • 字符串
    • 数字
    • 标识符
    • 运营商

    而 mysql_real_escape_string() 只转义其中的 一个。在所有其他三种情况下,您的查询绝对是赤裸裸的。好笑吧?

    最令人失望的部分: 我知道所有这些终极知识都是徒劳的,很少有菜鸟会读到,而且通常不会改变 PHP 社区的整体知识水平,也不会特别回答质量问题。 :(

    【讨论】:

    • 嗯,首先重要的是它是第二个,因为它不是第一个。我只是想知道数据库输出是否必须再次转义,现在我知道我必须这样做。这使得它在第 2、第 3、第 4 和以后都是一样的。其次,您说我必须在“它所属的数据,用户输入”上使用stripslashes。但这就是我使用函数的方式,即:WHERE field = '".sql::escape($input)."',那不安全吗?
    • 我读了你的整篇文章,我又读了一遍。我只是不完全遵循它。你是说我应该只对原始输入进行剥离,然后将 real_escape 函数留给所有其他数据库交互?
    • 没错!看,stripslashes与安全无关。无论使用与否,你都是安全的。它属于数据完整性,而不是安全性。但如果魔术引号打开,它会破坏你的数据。即使根本没有使用数据库,您也必须始终从用户输入中删除它。
    • @Kokos 还注意到我回答的第 3 部分。 sql::escape 不应用于“所有数据库交互”,而应仅用于转义字符串。用数字它不会帮助你。以及字段名称。
    • 是的,我读过,但我不明白其中的区别。字段名不是我让用户自己输入的,不是任何恶意输入都会自动变成字符串吗?我没有看到任何人以数字格式编写 SQL 查询。当整数不会中断时,以什么方式将整数放入mysql_real_escape_string() 是不好的?它可能会改变变量类型,但无论如何这在 PHP 中是非常松散的,或者我是一个巨大的菜鸟,因为我只是接受它而不是自己让它变得非常严格,因为我没有看到危险。
    猜你喜欢
    • 2014-02-02
    • 1970-01-01
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 2015-06-24
    • 2014-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多