htmlspecialchars和mysql_real_escape_string能保证我的PHP代码免于注入吗?

时间:2008-09-21 08:58:26

标签: php security xss sql-injection

今天早些时候,有人问过input validation strategies in web apps

在撰写本文时,最佳答案仅在PHP使用htmlspecialcharsmysql_real_escape_string时提出建议。

我的问题是:这总是足够的吗?还有更多我们应该知道的吗?这些功能在哪里崩溃?

6 个答案:

答案 0 :(得分:238)

说到数据库查询,请始终尝试使用准备好的参数化查询。 mysqliPDO库支持此功能。这比使用mysql_real_escape_string等转义函数更安全。

是的,mysql_real_escape_string实际上只是一个字符串转义函数。它不是一个神奇的子弹。它所做的只是逃避危险的字符,以便在单个查询字符串中使用它们是安全的。但是,如果您事先没有对输入进行消毒,那么您将容易受到某些攻击媒介的攻击。<​​/ p>

想象一下以下SQL:

$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);

你应该能够看到这很容易受到攻击 想象一下id参数包含常见的攻击向量:

1 OR 1=1

在那里没有风险的字符进行编码,因此它将直接通过转义过滤器。离开我们:

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

允许攻击者在这个完全虚构的示例中返回第一个管理员的详细信息。

虽然这些功能很有用,但必须小心使用。您需要确保在某种程度上验证所有Web输入。在这种情况下,我们看到我们可以被利用,因为我们没有检查我们用作数字的变量,实际上是数字。在PHP中,您应该广泛使用一组函数来检查输入是整数,浮点数,字母数字等。但是当涉及到SQL时,请注意准备语句的大部分值。如果它是一个准备好的语句,上面的代码就是安全的,因为数据库函数已经知道1 OR 1=1不是有效的文字。

至于htmlspecialchars()。那是一个独立的雷区。

PHP中存在一个真正的问题,即它具有一系列与html相关的不同转义函数,并且没有明确指导哪些函数可以做什么。

首先,如果你在HTML标签内,那你就遇到了麻烦。看看

echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';

我们已经在HTML标记内,因此我们不需要&lt;或者&gt;做任何危险的事情。我们的攻击向量可能只是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='...' />

在这些情况下,没有灵丹妙药,你只需要自己调整输入。如果你试图过滤掉坏的角色,你肯定会失败。采取白名单的方法,只让通过好的字符。请查看XSS cheat sheet有关不同向量的示例

即使您在HTML标记之外使用htmlspecialchars($string),您仍然容易受到多字节字符集攻击媒介的攻击。<​​/ p>

最有效的方法是使用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

答案 1 :(得分:10)

除了Cheekysoft的优秀答案:

  • 是的,他们会保证您的安全,但前提是他们的使用绝对正确。错误地使用它们仍然容易受到攻击,并且可能存在其他问题(例如数据损坏)
  • 请使用参数化查询(如上所述)。您可以通过例如PDO或通过像PEAR DB这样的包装器
  • 确保magic_quotes_gpc和magic_quotes_runtime始终处​​于关闭状态,并且永远不会被意外打开,甚至不会短暂打开。这些是PHP开发人员为防止安全问题(破坏数据)而早期且被误导的尝试。

防止HTML注入(例如跨站点脚本)并没有真正的灵丹妙药,但如果您使用库或模板系统输出HTML,则可能更容易实现。阅读有关如何妥善解决问题的文档。

在HTML中,需要根据上下文对事物进行不同的转义。对于放入Javascript中的字符串尤其如此。

答案 2 :(得分:3)

我肯定会同意上述帖子,但我有一个小问题可以回答Cheekysoft的回答,具体来说:

  

说到数据库查询,   总是尝试使用准备好的   参数化查询。 mysqli和   PDO库支持这一点。这是   比使用转义更安全   功能如   mysql_real_escape_string。

     

是的,mysql_real_escape_string是   实际上只是一个字符串转义   功能。它不是一个神奇的子弹。   它所能做的只是逃避危险   字符,以便它们可以   可以安全地在单个查询字符串中使用。   但是,如果你没有消毒你的   事先输入,然后你会   容易受到某些攻击媒介的影响。

     

想象一下以下SQL:

     

$ result =“SELECT字段FROM表   WHERE id =   ” .mysql_real_escape_string($ _ POST [ 'ID']);

     

你应该能够看到这是   容易被剥削。想象一下id   参数包含常见攻击   矢量:

     

1 OR 1 = 1

     

那里没有冒险的人物   编码,所以它将直接通过   通过逃逸过滤器。离开   我们:

     

SELECT字段FROM表WHERE id = 1   或1 = 1

我编写了一个快速的小函数,我放在我的数据库类中,它将删除任何不是数字的东西。它使用了preg_replace,所以有一些问题可能会更加优化,但是它可以用来捏......

function Numbers($input) {
  $input = preg_replace("/[^0-9]/","", $input);
  if($input == '') $input = 0;
  return $input;
}

所以不要使用

  

$ result =“SELECT字段FROM表WHERE id =”。mysqlrealescapestring(“1 OR 1 = 1”);

我会用

  

$ result =“SELECT字段FROM表WHERE id =”。Numbers(“1 OR 1 = 1”);

它可以安全地运行查询

  

SELECT字段FROM表WHERE id = 111

当然,这只是阻止它显示正确的行,但我不认为对于试图将sql注入您的网站的人来说这是一个大问题;)

答案 3 :(得分:2)

这个难题的一个重要部分是背景。如果您在查询中引用每个参数,那么发送“1 OR 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'

无效。由于您正在转义字符串,因此输入不会突破字符串上下文。我已经对MySQL的5.0.45版本进行了测试,并且对整数列使用字符串上下文不会导致任何问题。

答案 4 :(得分:2)

$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

在64位系统上运行良好,甚至更好。请注意您在处理大数字时遇到的系统限制,但对于数据库ID,99%的情况下这很有用。

您也应该使用单一功能/方法来清理您的值。即使这个函数只是mysql_real_escape_string()的包装器。为什么?因为有一天会发现您首选的清理数据方法,只需将其更新到一个地方,而不是在系统范围内查找和替换。

答案 5 :(得分:-3)

为什么,为什么,你在sql语句中包含用户输入的引号?似乎很傻到不!在你的sql语句中包含引号会呈现&#34; 1或1 = 1&#34;没有成功的尝试,不是吗?

所以现在,你要说,&#34;如果用户在输入中包含引号(或双引号)怎么办?&#34;

嗯,很容易解决这个问题:只需删除用户输入&#39; d引号。例如:input =~ s/'//g;。现在,无论如何,在我看来,用户输入将是安全的......