代码之家  ›  专栏  ›  技术社区  ›  Cheekysoft Moz Morris

htmlspecialchars和mysql_real_escape_string是否确保我的PHP代码不被注入?

  •  114
  • Cheekysoft Moz Morris  · 技术社区  · 16 年前

    今天早些时候,有人就 input validation strategies in web apps .

    在写作时,最重要的答案是 PHP 只是使用 htmlspecialchars mysql_real_escape_string .

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

    6 回复  |  直到 7 年前
        1
  •  238
  •   Community T.Woody    7 年前

    当涉及到数据库查询时,总是尝试使用准备好的参数化查询。这个 mysqli PDO 库支持这一点。这比使用诸如 mysql_real_escape_string .

    对, mysql_real_escape_字符串 实际上只是一个字符串转义函数。这不是一颗神奇的子弹。它所要做的就是转义危险字符,以便在单个查询字符串中安全地使用它们。但是,如果您没有事先对输入进行消毒,那么您将容易受到某些攻击向量的攻击。

    想象一下下面的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标记内,因此不需要<或>来做任何危险的事情。我们的攻击向量可能只是 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 例如不同的向量

    即使你使用 htmlspecialchars($string) 在HTML标记之外,您仍然容易受到多字节字符集攻击向量的攻击。

    最有效的方法是使用mb_convert_编码和htmlentities的组合,如下所示。

    $str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
    $str = htmlentities($str, ENT_QUOTES, 'UTF-8');
    

    即使这样,由于IE6处理UTF的方式,它仍然容易受到攻击。但是,在IE6使用率下降之前,您可以返回到更有限的编码,如ISO-8859-1。

    有关多字节问题的更深入研究,请参见 https://stackoverflow.com/a/12118602/1820

        2
  •  10
  •   MarkR    16 年前

    除了Cheekysoft出色的回答:

    • 是的,他们会保护你的安全,但前提是他们的使用绝对正确。不正确地使用它们,您仍然很容易受到攻击,可能还有其他问题(例如数据损坏)
    • 请使用参数化查询(如上所述)。您可以通过PDO或PearDB之类的包装器来使用它们。
    • 确保Magic_Quotes_GPC和Magic_Quotes_运行时始终处于关闭状态,并且不会意外打开,甚至不会短暂打开。这是PHP开发人员为防止安全问题(破坏数据)而进行的早期且严重误导的尝试。

    对于防止HTML注入(例如跨站点脚本),没有真正的银弹,但是如果您使用库或模板系统来输出HTML,则可以更容易地实现它。阅读相关文档,了解如何适当地避开问题。

    在HTML中,根据上下文的不同,需要对事物进行不同的转义。尤其是在将字符串放入JavaScript时。

        3
  •  3
  •   BrilliantWinter    16 年前

    我肯定同意以上的观点,但我有一个小问题要补充,以回应奇基索夫的回答,特别是:

    当涉及到数据库查询时, 总是试着用准备好的 参数化查询。MyQuLi和 PDO库支持这一点。这是 比使用转义更安全 功能,例如 mysql_real_escape_字符串。

    是的,mysql-real-escape-string是 实际上只是字符串转义 功能。这不是一颗神奇的子弹。 它所要做的就是逃避危险 字符的顺序 在单个查询字符串中使用安全。 但是,如果你不消毒 事先输入,然后 易受某些攻击向量的攻击。

    想象一下下面的SQL:

    $result=从表中选择字段 其中ID= .mysql_real_escape_string($_post['id']);

    你应该能看到这是 易受剥削。想象一下身份证 包含常见攻击的参数 矢量:

    1或1=1

    里面没有危险的角色 编码,这样它就可以直接通过 通过溢出的过滤器。离开 我们:

    从ID=1的表中选择字段 或1=1

    我编写了一个快速的小函数,放在我的数据库类中,它可以去掉任何不是数字的东西。它使用了preg ou replace,所以有一个更优化的prob功能,但它在紧要关头工作…

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

    所以不用

    $result=“从表中选择字段,其中id=”.mysqlRealEscapeString(“1或1=1”);

    我会用

    $result=“从表中选择字段,其中id=”数字(“1或1=1”);

    它可以安全地运行查询

    从ID=111的表中选择字段

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

        4
  •  2
  •   Lucas Oman    16 年前

    这个难题的一个重要部分是上下文。如果引用查询中的每个参数,则发送“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'
    

    这是无效的。因为您要转义字符串,所以输入不能脱离字符串上下文。我已经测试了5.0.45版本的MySQL,使用一个整型列的字符串上下文不会导致任何问题。

        5
  •  2
  •   cnizzardini    13 年前
    $result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];
    

    在64位系统上工作得很好,甚至更好。但是要注意系统在处理大量数据方面的限制,但是对于数据库ID来说,这在99%的时间里是有效的。

    您还应该使用一个函数/方法来清除您的值。即使这个函数只是mysql_real_escape_string()的包装器。为什么?因为有一天,当发现对您首选的清理数据方法的利用时,您只需要更新一个位置,而不需要在整个系统范围内进行查找和替换。

        6
  •  -3
  •   Jarett L    7 年前

    为什么,哦,为什么,你愿意吗? 在SQL语句中包括用户输入的引号?似乎很傻不去!在SQL语句中包含引号会使“1或1=1”成为一种徒劳的尝试,不是吗?

    现在,您将说,“如果用户在输入中包含一个引号(或双引号),该怎么办?”

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