是否需要从数据库中转义用户输入?

时间:2011-09-16 07:53:31

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

所以我知道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);

}

4 个答案:

答案 0 :(得分:14)

  

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

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

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

答案 1 :(得分:5)

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

逃离弦乐对许多人来说听起来很神奇,就像屏蔽了一些神秘的危险,但事实上它并不神奇。这只是启用查询处理特殊字符的方法。

最好的只是看看逃避真正做到了什么。假设输入字符串是:

'); DROP `table` --
逃跑后

\'); DROP `table` --
实际上它只逃脱了单斜杠。这是你需要确保的唯一一件事 - 当你在查询中插入字符串时,语法就可以了!

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

这就像危险盾牌之类的东西一样神奇,它只是为了确保结果查询具有正确的语法!(当然如果没有,它可以被利用)

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

'); DROP `table` --

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

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

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

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

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. JS​​ON
  5. mysql正则表达式
  6. php正则表达式
  7. 对于每种情况,您需要不同的引用,因为每种用法都存在于不同的语法上下文中。这也意味着引用不应该在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()

答案 2 :(得分:4)

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

你这个问题绝对是错误的 如果它是第一个或第二个或第100个,则不必计算他的查询 用户输入也是如此:无关紧要,数据来自哪里!

数据目的地,而非来源应该是您关注的问题。这个字符串是否会进入数据库?逃避吧!没问题。此规则简单明了,无需查询或任何查询。

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

  

MySQL会自动逃避输出还是类似的东西?

这是一个非常糟糕的主意。有趣的是,你通过应用get_magic_quotes_gpc()来解决代码中相同想法的后果。如果没有这样的自动转义,这些神奇的引用是什么?

<强>两个
而且,在你的转义函数中使用get_magic_quotes_gpc()是一个非常糟糕的主意:)

假设你有魔术引号并使用你的函数来保护你的“第二个查询”。并且有一些blob在数据中包含\'序列。您的函数将删除斜杠并破坏数据。事实上,stripslashes与任何转义函数完全无关。在用户输入

上单独对其所属的数据执行此操作

<强>三
mysql_real_escape_string()不是“让一切安全”的神奇功能。事实上,要创建动态mysql查询,必须要逃避四种种类的数据:

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

而mysql_real_escape_string()仅转发一个。在其他三种情况下,您的查询绝对是裸体的。好笑,嗯?

最令人失望的部分: 我知道所有这些终极知识都是徒劳的,几乎不会被少数新手阅读,也永远不会改变PHP社区的整体知识水平,也不会特别回答SO的质量问题。 :(

答案 3 :(得分:2)

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

  1. 您可以使用PDO的准备函数来编译您的查询。如果您需要使用不同的输入发出相同的查询(通常就是这种情况),这是有效的。因此,编译一次并执行多次。
  2. 使用 prepare 编译查询还有其他不错的效果。编译查询后,数据库引擎知道查询的确切语法结构,并且不允许任何更改此语法结构的输入。这很好,因为在SQL注入中,注入的输入会更改查询的语法。
  3. 警告:这不会阻止所有类型的SQL注入,但它会阻止最常见的类型。

    参考文献:

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