htmlspecialchars和mysql_real_escape_string保持我的PHP代码安全注入?

今天早些时候,有人问到在web应用程序中inputvalidation策略的问题。

在撰写本文的时候,最好的答案是在PHP使用htmlspecialcharsmysql_real_escape_string

我的问题是:这足够吗? 还有更多我们应该知道的吗? 这些function在哪里分解?

说到数据库查询,总是尝试使用准备好的参数化查询。 mysqliPDO库支持这一点。 这比使用诸如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将是安全的。