首页 文章

如何使用HTML / PHP防止XSS?

提问于
浏览
207

如何仅使用HTML和PHP来阻止XSS(跨站点脚本)?

我已经看过很多关于这个主题的帖子,但我还没有找到一篇清晰简明地说明如何实际防止XSS的文章 .

9 回答

  • 1

    Basically you need to use the function htmlspecialchars() whenever you want to output something to the browser that came from the user input.

    使用此函数的正确方法是这样的:

    echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
    

    Google Code University也有关于网络安全的这些非常有教育意义的视频:

  • 0

    许多框架以各种方式帮助处理XSS . 当您自己滚动或者有一些XSS问题时,我们可以利用filter_input_array(在PHP 5> = 5.2.0,PHP 7中可用) . 我通常会将此片段添加到我的SessionController中,因为所有调用都会在任何其他控制器之前通过与数据交互 . 以这种方式,所有用户输入在1个中心位置被消毒 . 如果这是在项目开始时或数据库中毒之前完成的,那么在输出时不应该出现任何问题...停止垃圾进入,垃圾进出 .

    /* Prevent XSS input */
    $_GET   = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
    $_POST  = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
    /* I prefer not to use $_REQUEST...but for those who do: */
    $_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST;
    

    以上将删除 ALL HTML和脚本标记 . 如果您需要一个允许安全标签的解决方案,基于白名单,请查看HTML Purifier .


    如果您的数据库已经中毒或者您希望在输出时处理XSS,OWASP建议为 echo 创建自定义包装函数,并使用它在任何地方输出用户提供的值:

    //xss mitigation functions
    function xssafe($data,$encoding='UTF-8')
    {
       return htmlspecialchars($data,ENT_QUOTES | ENT_HTML401,$encoding);
    }
    function xecho($data)
    {
       echo xssafe($data);
    }
    
  • 2

    我最喜欢的 OWASP 引用之一是 Cross-Site Scripting 解释,因为虽然有大量的XSS攻击向量,但是以下几条规则可以大大防御它们中的大多数!

    这是 PHP Security Cheat Sheet

  • 4

    按优先顺序排列:

    • 如果您使用的是模板引擎(例如Twig,Smarty,Blade),请检查它是否提供了上下文相关的转义 . 我根据经验知道Twig的确如此 . {{ var|e('html_attr') }}

    • 如果要允许HTML,请使用HTML Purifier . 即使您认为您只接受Markdown或ReStructuredText,您仍然希望净化这些标记语言输出的HTML .

    • 否则,请使用 htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset) 并确保文档的其余部分使用与 $charset 相同的字符集 . 在大多数情况下, 'UTF-8' 是所需的字符集 .

    另外,请确保escape on output, not on input .

  • 236

    PHP 上使用 htmlspecialchars . 在HTML上尽量避免使用:

    element.innerHTML = “…”; element.outerHTML = “…”; document.write(…); document.writeln(…);

    其中 var 由用户控制 .

    显然也试着避免 eval(var) ,如果你必须使用它们中的任何一个然后尝试 JS 逃避它们, HTML 逃避它们你可能不得不做更多但是对于基础知识这应该足够了 .

  • 7

    将此作为SO Documentation beta的合并参考进行交叉发布,该测试将脱机 .

    问题

    跨站点脚本是Web客户端意外执行远程代码 . 如果任何Web应用程序从用户获取输入并直接在网页上输出,则可能会将自身暴露给XSS . 如果输入包括HTML或JavaScript,则当Web客户端呈现此内容时,可以执行远程代码 .

    例如,如果第三方包含JavaScript文件:

    // http://example.com/runme.js
    document.write("I'm running");
    

    PHP应用程序直接输出传递给它的字符串:

    <?php
    echo '<div>' . $_GET['input'] . '</div>';
    

    如果未经检查的GET参数包含 <script src="http://example.com/runme.js"></script> ,则PHP脚本的输出将为:

    <div><script src="http://example.com/runme.js"></script></div>
    

    第三方JavaScript将运行,用户将在网页上看到“我正在运行” .

    解决方案

    作为一般规则,永远不要相信来自客户的输入 . 每个GET,POST和cookie值都可以是任何值,因此应该进行验证 . 输出任何这些值时,请将它们转义,以便不会以意外方式对它们进行求值 .

    请记住,即使在最简单的应用程序中,数据也可以移动,并且很难跟踪所有来源 . 因此,始终逃避输出是最佳做法 .

    PHP根据上下文提供了一些转义输出的方法 .

    过滤功能

    PHPs Filter Functions允许php脚本的输入数据为sanitizedmany ways中的validated . 它们在保存或输出客户端输入时很有用 .

    HTML编码

    htmlspecialchars 会将任何"HTML special characters"转换为其HTML编码,这意味着它们将不会被处理为标准HTML . 要使用此方法修复上一个示例:

    <?php
    echo '<div>' . htmlspecialchars($_GET['input']) . '</div>';
    // or
    echo '<div>' . filter_input(INPUT_GET, 'input', FILTER_SANITIZE_SPECIAL_CHARS) . '</div>';
    

    输出:

    <div>&lt;script src=&quot;http://example.com/runme.js&quot;&gt;&lt;/script&gt;</div>
    

    <div> 标记内的所有内容都不会被浏览器解释为JavaScript标记,而是作为简单的文本节点 . 用户将安全地看到:

    <script src="http://example.com/runme.js"></script>
    

    网址编码

    在输出动态生成的URL时,PHP提供 urlencode 函数以安全地输出有效的URL . 因此,例如,如果用户能够输入成为另一个GET参数的一部分的数据:

    <?php
    $input = urlencode($_GET['input']);
    // or
    $input = filter_input(INPUT_GET, 'input', FILTER_SANITIZE_URL);
    echo '<a href="http://example.com/page?input="' . $input . '">Link</a>';
    

    任何恶意输入都将转换为编码的URL参数 .

    使用专门的外部库或OWASP AntiSamy列表

    有时您会想要发送HTML或其他类型的代码输入 . 您需要维护一份授权单词(白名单)和未经授权的单词列表(黑名单) .

    您可以下载OWASP AntiSamy website中提供的标准列表 . 每个列表适合特定类型的交互(ebay api,tinyMCE等...) . 它是开源的 .

    现有的库可以过滤HTML并防止针对一般情况的XSS攻击,并且至少与AntiSamy列表一样易于使用 . 比如你有HTML Purifier

  • 3

    其中一个最重要的步骤是在处理和/或渲染回浏览器之前清理任何用户输入 . PHP有一些可以使用的“filter”函数 .

    XSS攻击通常具有的形式是插入一些链接到一些包含用户恶意意图的场外JavaScript . 阅读更多相关信息here .

    您还需要测试您的网站 - 我可以推荐Firefox附加组件XSS Me .

  • 35

    您还可以通过 header(...) 设置一些与XSS相关的HTTP响应标头

    X-XSS-Protection“1; mode = block”

    可以肯定的是,浏览器XSS保护模式已启用 .

    Content-Security-Policy“default-src'self'; ...”

    启用浏览器端内容安全性 . 有关内容安全策略(CSP)的详细信息,请参阅此内容:http://content-security-policy.com/特别设置CSP以阻止内联脚本和外部脚本源有助于XSS .

    有关Webapp安全性的一堆有用的HTTP响应 Headers ,请查看OWASP:https://www.owasp.org/index.php/List_of_useful_HTTP_headers

  • 14
    <?php
    function xss_clean($data)
    {
    // Fix &entity\n;
    $data = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $data);
    $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
    $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
    $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
    
    // Remove any attribute starting with "on" or xmlns
    $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
    
    // Remove javascript: and vbscript: protocols
    $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
    $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
    $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
    
    // Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
    $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
    $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
    $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);
    
    // Remove namespaced elements (we do not need them)
    $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
    
    do
    {
        // Remove really unwanted tags
        $old_data = $data;
        $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
    }
    while ($old_data !== $data);
    
    // we are done...
    return $data;
    }
    

相关问题