如何使用HTML / PHP防止XSS?

时间:2010-01-03 20:09:09

标签: php xss

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

我在这个主题上看过很多其他帖子,但我没有找到一篇清晰简明地说明如何实际阻止XSS的文章。

9 个答案:

答案 0 :(得分:263)

基本上,只要您想要将某些内容输出到来自用户输入的浏览器,您就需要使用函数htmlspecialchars()

使用此功能的正确方法是这样的:

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

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

答案 1 :(得分:36)

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

这是 PHP Security Cheat Sheet

答案 2 :(得分:14)

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

XSS攻击通常具有的形式是向一些包含用户恶意意图的异地javascript插入链接。阅读更多相关信息here

您还需要测试您的网站 - 我可以推荐Firefox插件XSS Me

答案 3 :(得分:9)

按优先顺序排列:

  1. 如果您使用的是模板引擎(例如Twig,Smarty,Blade),请检查它是否提供了上下文相关的转义。我从Twig的经验中知道。 {{ var|e('html_attr') }}
  2. 如果您想允许HTML,请使用HTML Purifier。即使您认为您只接受Markdown或ReStructuredText,您仍然希望净化这些标记语言输出的HTML。
  3. 否则,请使用htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)并确保文档的其余部分使用与$charset相同的字符集。在大多数情况下,'UTF-8'是所需的字符集。
  4. 另外,请确保escape on output, not on input

答案 4 :(得分:6)

将此文档作为SO文档测试版中的合并参考文件进行交叉发布。

问题

跨站点脚本是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将运行,用户将看到&#34;我正在运行&#34;在网页上。

解决方案

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

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

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

过滤功能

PHPs Filter Functions允许php脚本的输入数据在sanitized中为validatedmany ways。它们在保存或输出客户端输入时非常有用。

HTML编码

htmlspecialchars会转换任何&#34; HTML特殊字符&#34;进入他们的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

答案 5 :(得分:4)

许多框架以各种方式帮助处理XSS。在滚动自己或者有一些XSS问题时,我们可以利用filter_input_array(在PHP 5&gt; = 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;

以上内容将删除所有 HTML&amp;脚本标签。如果您需要基于白名单的安全标记解决方案,请查看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);
}

答案 6 :(得分:3)

<?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;
}

答案 7 :(得分:0)

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

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

其中var 由用户控制。

显然也试着避免eval(var), 如果你必须使用它们中的任何一个,那么尝试 JS 转义它们, HTML 逃避它们,你可能需要做更多的事情,但是对于基础知识来说这应该足够了。

答案 8 :(得分:0)

保护输入的最佳方法是使用htmlentities函数。 示例:

htmlentities($target, ENT_QUOTES, 'UTF-8');

您可以获得更多信息here