如何防止XSS与HTML/PHP?

我如何防止XSS(跨站点脚本)只使用HTML和PHP?

关于这个主题,我已经看到了许多其他的帖子,但是我还没有找到一篇文章能够清楚而简洁地说明如何实际防止XSS。

312324 次浏览

最重要的步骤之一是在用户输入被处理和/或呈现回浏览器之前对其进行消毒。PHP有一些&;过滤器"可以使用的函数。

XSS攻击的形式通常是向用户插入一些包含恶意意图的场外javascript的链接。阅读更多关于它在这里

你还需要测试你的站点——我可以推荐Firefox插件[XSS Me]。看起来简单的XSS现在是要走的路。

基本上你需要使用函数htmlspecialchars()当你想输出一些来自用户输入的东西到浏览器时。

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

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

谷歌Code University也有这些非常有教育意义的Web安全视频:

按偏好顺序排列:

  1. 如果你正在使用模板引擎(例如Twig, Smarty, Blade),检查它是否提供上下文敏感转义。根据我的经验,特威格是这样的。\{\{ var|e('html_attr') }}
  2. 如果你想允许HTML,使用HTML净化器。即使您认为您只接受Markdown或ReStructuredText,您仍然希望净化这些标记语言输出的HTML。
  3. 否则,使用htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)并确保文档的其余部分使用与$charset相同的字符集。在大多数情况下,'UTF-8'是所需的字符集。

另外,确保你转义是在输出上,而不是在输入上

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

你也可以通过header(...)设置一些XSS相关的HTTP响应头

X-XSS-Protection”1;模式=块”

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

Content-Security-Policy "default-src 'self';……”

启用浏览器端内容安全性。内容安全策略(CSP)的详细信息请看这个:http://content-security-policy.com/ 特别是设置CSP来阻止内联脚本和外部脚本源有助于对抗XSS。< / p >

关于你的web应用程序的安全性的一堆有用的HTTP响应头,请看OWASP: https://www.owasp.org/index.php/List_of_useful_HTTP_headers

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

< p > <代码>元素。innerHTML = "…"; 元素。outerHTML = "…"; document . write(…); document.writeln(…);< /代码> < / p >

其中var由用户控制

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

交叉发布这作为一个统一的参考从SO文档测试版下线。

问题

跨站脚本是web客户端对远程代码的意外执行。任何web应用程序都可能向XSS公开自己,如果它接受用户的输入并直接在web页面上输出。如果输入包含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或PUT内容以及cookie值都可以是任何东西,因此都应该进行验证。当输出任何这些值时,请转义它们,以便它们不会以意外的方式计算。

请记住,即使在最简单的应用程序中,数据也可以四处移动,很难跟踪所有的源。因此,总是转义输出是一个最佳实践。

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

过滤功能

PHPs过滤功能允许php脚本的输入数据为消毒验证中的很多方面。它们在保存或输出客户端输入时非常有用。

HTML编码

htmlspecialchars将转换任何“HTML特殊字符”;到他们的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编码

当输出动态生成的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网站下载标准列表。每个列表都适合特定类型的交互(ebay api, tinyMCE等)。而且它是开源的。

现在有一些库可以过滤HTML,防止XSS攻击,并且至少和AntiSamy列表一样好用。 例如,你有HTML净化器

许多框架以各种方式帮助处理XSS。当滚动您自己的或如果有一些XSS问题时,我们可以利用filter_input_array(在PHP 5 >= 5.2.0, PHP 7中可用)。 我通常会将这个片段添加到我的SessionController中,因为在任何其他控制器与数据交互之前,所有调用都会经过它。通过这种方式,所有用户输入都在一个中心位置得到了清理。如果这是在项目开始或数据库中毒之前完成的,那么在输出时应该不会有任何问题……阻止垃圾输入,垃圾输出。 < / p >
/* 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 &脚本标签。如果你需要一个基于白名单的安全标签的解决方案,请查看HTML净化器


如果你的数据库已经中毒,或者你想在输出时处理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);
}
保护你的输入的最好方法是使用htmlentities函数。 例子:< / p >
htmlentities($target, ENT_QUOTES, 'UTF-8');

你可以得到更多的信息在这里