htmlspecialchars和mysql_real_escape_string保持我的PHP代码安全注入?
今天早些时候,有人问到在web应用程序中inputvalidation策略的问题。
在撰写本文的时候,最好的答案是在PHP
使用htmlspecialchars
和mysql_real_escape_string
。
我的问题是:这足够吗? 还有更多我们应该知道的吗? 这些function在哪里分解?
说到数据库查询,总是尝试使用准备好的参数化查询。 mysqli
和PDO
库支持这一点。 这比使用诸如mysql_real_escape_string
转义函数更安全。
是的, mysql_real_escape_string
实际上只是一个string转义函数。 这不是一个神奇的子弹。 它所要做的就是转义危险字符,以便在单个查询string中安全使用。 但是,如果您事先没有对您的input进行清理,那么您将很容易受到某些攻击媒介的攻击。
想象下面的SQL:
$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);
你应该能够看到这是易受攻击的。
想象一下, id
参数包含常见的攻击vector:
1 OR 1=1
在那里没有危险的字符编码,所以它会直接通过逃跑的filter。 离开我们:
SELECT fields FROM table WHERE id= 1 OR 1=1
这是一个可爱的SQL注入向量,并允许攻击者返回所有的行。 要么
1 or is_admin=1 order by id limit 1
生产
SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1
这允许攻击者在这个完全虚构的例子中返回第一个pipe理员的详细信息。
虽然这些function是有用的,但必须小心使用。 您需要确保所有网页input在一定程度上得到validation。 在这种情况下,我们看到我们可以被利用,因为我们没有检查我们用作数字的variables,实际上是数字。 在PHP中,您应该广泛使用一组函数来检查input是整数,浮点数,字母数字等。但是对于SQL,请注意准备语句的大部分值。 如果数据库函数已经知道1 OR 1=1
不是有效的字面量,那么上面的代码将是安全的。
至于htmlspecialchars()
。 这是它自己的雷区。
在PHP中有一个真正的问题,它有一个完整的select不同的HTML相关的转义函数,并没有明确的指导哪些function做什么。
首先,如果你在一个HTML标签里,你真的很麻烦。 看着
echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';
我们已经在一个HTML标签内,所以我们不需要<或>来做任何危险的事情。 我们的攻击vector可能只是javascript:alert(document.cookie)
现在生成的HTML看起来像
<img src= "javascript:alert(document.cookie)" />
攻击得到了直接的通过。
它变得更糟。 为什么? 因为htmlspecialchars
(当这样调用时)只能编码双引号而不是单引号。 所以,如果我们有
echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";
我们的邪恶攻击者现在可以注入全新的参数
pic.png' onclick='location.href=xxx' onmouseover='...
给我们
<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />
在这些情况下,没有什么灵丹妙药,你只需要自己挑选input。 如果你尝试过滤出不好的字符,你肯定会失败。 采取白名单的方式,只通过字符是好的。 查看XSS备忘单 ,了解各种向量可能的示例
即使您在HTML标记之外使用htmlspecialchars($string)
,仍然容易受到多字节字符集攻击媒介的攻击。
您可以最有效地使用mb_convert_encoding和htmlentities的组合,如下所示。
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8'); $str = htmlentities($str, ENT_QUOTES, 'UTF-8');
即使这样也会使IE6容易受到攻击,因为它处理UTF。 但是,您可能会回到更有限的编码,如ISO-8859-1,直到IE6使用下降。
有关多字节问题的更深入研究,请参阅https://stackoverflow.com/a/12118602/1820
除了Cheekysoft的优秀答案:
- 是的,他们会保持你的安全,但只有当他们的使用绝对正确。 使用它们不正确,你仍然是脆弱的,并可能有其他问题(例如数据损坏)
- 请改用参数化查询(如上所述)。 你可以通过例如PDO或PEAR DB等包装来使用它们
- 确保magic_quotes_gpc和magic_quotes_runtime始终处于closures状态,并且不会意外打开,甚至不会短暂地打开。 这些是PHP开发人员为了防止安全问题(破坏数据)而做出的一个早期的误导性尝试,
对于防止HTML注入(例如跨站点脚本)来说,并没有真正的银弹,但是如果使用库或模板系统来输出HTML,则可能更容易实现。 阅读有关如何正确转义的文档。
在HTML中,事情需要根据上下文以不同的方式转义。 对于放入Javascript的string尤其如此。
我肯定会同意上面的post,但是我还有一件小事要回复Cheekysoft的回答,具体是:
说到数据库查询,总是尝试使用准备好的参数化查询。 mysqli和PDO库支持这一点。 这比使用诸如mysql_real_escape_string之类的转义函数更安全。
是的,mysql_real_escape_string实际上只是一个string转义函数。 这不是一个神奇的子弹。 它所要做的就是转义危险字符,以便在单个查询string中安全使用。 但是,如果您事先没有对您的input进行清理,那么您将很容易受到某些攻击媒介的攻击。
想象下面的SQL:
$ result =“select字段从表WHERE id =”.mysql_real_escape_string($ _ POST ['id']);
你应该能够看到这是易受攻击的。 想象一下,id参数包含常见的攻击vector:
1或1 = 1
在那里没有危险的字符编码,所以它会直接通过逃跑的filter。 离开我们:
SELECT字段从表WHERE id = 1或1 = 1
我编写了一个快速的小函数,我把它放在我的数据库类中,它将删除所有不是数字的东西。 它使用preg_replace,所以有可能多一点优化的function,但它在一个捏…
function Numbers($input) { $input = preg_replace("/[^0-9]/","", $input); if($input == '') $input = 0; return $input; }
所以,而不是使用
$ result =“从表中select字段WHERE id =”.mysqlrealescapestring(“1 OR 1 = 1”);
我会用
$ result =“select字段从表WHERE id =”.Numbers(“1 OR 1 = 1”);
它会安全地运行查询
SELECT字段从表WHERE id = 111
当然,这只是停止显示正确的行,但我不认为这是一个大问题谁试图注入到您的网站的SQL;)
这个难题的一个重要部分是上下文。 有人发送“1或1 = 1”作为ID是不是一个问题,如果你引用你的查询中的每个参数:
SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"
其结果是:
SELECT fields FROM table WHERE id='1 OR 1=1'
这是无效的。 由于您正在转义string,所以input不能脱离string上下文。 我已经testing了这个MySQL的版本5.0.45,并使用string上下文的整数列不会导致任何问题。
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];
运行良好,在64位系统上更好。 但是要小心你的系统在处理大量数据方面的限制,但是对于数据库ID来说,这个工作在99%的时间内是非常有效的。
你应该使用一个单一的函数/方法来清理你的值。 即使这个函数只是mysql_real_escape_string()的包装器。 为什么? 因为有一天,如果发现利用您的首选清理数据的方法,则只需将其更新到一个位置,而不是在系统范围内find并replace。
为什么,哦,为什么,你会不会在你的sql语句中包含用户input的引号? 看起来挺傻的不! 包括你的sql语句中的引号会使“1或1 = 1”成为一个没有结果的尝试,不是吗?
所以现在,你会说,“如果用户在input中包含引号(或双引号),该怎么办?
好吧,很容易解决这个问题:只需删除用户input的报价。 例如: input =~ s/'//g;
。 现在,无论如何,在我看来,用户的input将是安全的。