所以我知道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是否自动转义它们的输出或类似的东西,或者我应该在第二个查询中也转义?
这是一个测试用例,但它在我的程序中以其他方式发生,我想知道对于这样的情况,安全性必须有多严格。
编辑
我的escape函数
static function escape($string){
if(get_magic_quotes_gpc())
$string = stripslashes($string);
return mysql_real_escape_string($string);
}
MySQL是否自动转义它们的输出或类似的东西,或者我应该在第二个查询中也转义?
在第二个查询中也需要转义。MySQL不会对其输出进行任何转义。
长话短说:MySQL字符串转义不会修改插入的字符串,它只是确保它不会对当前查询造成任何伤害。任何SQL注入尝试仍然保留在数据中。
是的,您也必须在第二个查询中转义字符串。
转义字符串对很多人来说听起来很神奇,就像躲避一些神秘的危险一样,但实际上并没有什么神奇的。这只是允许查询处理特殊字符的方法。
最好是看看转义到底做了什么。假设输入字符串是:
'); 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);
}
这是邪恶的第二个原因是因为没有像"通用引用"这样的东西。在引用时,总是引用某些特定输出的文本,例如:
- mysql查询的字符串值mysql查询的
- json mysql正则表达式 php正则表达式
like
表达式对于每种情况,需要不同的引用,因为每种用法出现在不同的语法上下文中。这也意味着不应该在PHP的输入处引用,而应该在特定的输出处引用!这就是为什么像magic_quotes_gpc
这样的功能被破坏的原因(永远不要忘记处理它,或者更好的是,确保它被关闭!!)。
那么,在这些特殊情况下应该使用什么方法来引用呢?(请随意纠正我,可能有更现代的方法,但这些对我来说是有效的)
-
mysql_real_escape_string($str)
-
mysql_real_escape_string(addcslashes($str, "%_"))
-
htmlspecialchars($str)
-
json_encode()
-仅用于utf8!我使用我的功能为iso-8859-2 -
mysql_real_escape_string(addcslashes($str, '^.[]$()|*+?{}'))
-在这种情况下不能使用preg_quote,因为反斜杠会被转义两次! -
preg_quote()
我想说这个问题的整个想法都是错误的。
你完全误解了这个问题。
一个人不需要计算他的查询,如果它是第一个、第二个或第100个。
同样适用于用户输入:这无关紧要,数据来自哪里!
数据目的地,而不是源应该是您关心的。这个字符串要到数据库吗?逃避它!没有问题。该规则简单明了,不需要查询计数或其他任何东西。
但这不仅仅是你问题的错误。
:
MySQL是否自动转义它们的输出或类似的东西?
这是个非常糟糕的主意。有趣的是,通过应用get_magic_quotes_gpc(),您正在与代码中相同思想的结果作斗争。如果不是自动转义,这些神奇的引号是什么?
二:
此外,在转义函数中使用get_magic_quotes_gpc()也是一个非常糟糕的主意:)
想象你有魔术引号,并使用你的函数来保护你的"第二个查询"。数据中存在一些包含'
序列的blob。函数将删除斜杠并破坏数据。实际上,stripslashes与任何转义函数完全没有关系。在属于它的数据上——在用户输入上单独执行。
Mysql_real_escape_string()不是"使一切安全"的魔法函数。实际上,要创建动态mysql查询,必须转义四种类型的数据:
- 字符串数字
- 操作符
而mysql_real_escape_string()只转义其中的一个。你的质疑在其他三个案子里都是赤裸裸的。有趣的,不是吗?
最令人失望的部分:我知道所有这些终极知识都是徒劳的,很少有新手会去读,也不会改变PHP社区的总体知识水平,也不会特别对SO给出高质量的答案。
如果可以的话,尝试使用PHP的PDO访问数据库。有两个重要的原因:
- 你可以使用PDO的prepare函数来编译你的查询。如果需要用不同的输入发出相同的查询(通常是这种情况),这是有效的。所以,编译一次,执行多次。
- 用prepare编译查询还有其他很好的效果。一旦编译了查询,数据库引擎就知道查询的确切语法结构,并且不允许任何改变该语法结构的输入。这很好,因为在SQL注入中,注入的输入会改变查询的语法。
警告:这并不能阻止所有类型的SQL注入,但它可以阻止最常见的类型。
引用:
- PDO准备的语句是否足以防止SQL注入?
- http://php.net/manual/en/pdo.prepare.php