提问人:TimTim 提问时间:1/4/2010 最后编辑:Peter MortensenTimTim 更新时间:8/3/2023 访问量:333907
如何防止 HTML/PHP 的 XSS?
How can I prevent XSS with HTML/PHP?
答:
最重要的步骤之一是在处理和/或呈现回浏览器之前清理任何用户输入。PHP有一些可以使用的“过滤器”功能。
XSS 攻击通常具有的形式是插入指向某些异地 JavaScript 代码的链接,该代码包含对用户的恶意意图。在这里阅读更多关于它的信息。
您还需要测试您的网站。看起来Easy XSS现在是要走的路。
评论
基本上,每当你想在 HTML 上下文中向浏览器输出一些东西时,你都需要使用函数 htmlspecialchars()。
使用此函数的正确方法是这样的:
echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
Google Code University 也有这些关于网络安全的非常有教育意义的视频:
评论
htmlspecialchars()
按优先顺序:
- 如果您使用的是模板引擎(例如 Twig、Smarty、Blade),请检查它是否提供上下文相关转义。我从经验中知道 Twig 确实如此。
{{ var|e('html_attr') }}
- 如果要允许 HTML,请使用 HTML Purifier。即使你认为你只接受 Markdown 或 ReStructuredText,你仍然希望净化 HTML 这些标记语言的输出。
- 否则,请使用并确保文档的其余部分使用与 相同的字符集。在大多数情况下,是所需的字符集。
htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)
$charset
'UTF-8'
此外,请确保在输出时转义,而不是在输入时转义。
评论
<?php
function xss_clean($data)
{
// Fix &entity\n;
$data = str_replace(array('&','<','>'), array('&amp;','&lt;','&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;
}
评论
preg_replace
eval
您还可以通过以下方式设置一些与 XSS 相关的 HTTP 响应标头:header(...)
X-XSS-保护“1;mode=block”
可以肯定的是,浏览器 XSS 保护模式已启用。
Content-Security-Policy “默认 src 'self';..."
以启用浏览器端内容安全性。有关内容安全策略 (CSP) 的详细信息,请参阅此内容:
特别是将 CSP 设置为阻止内联脚本和外部脚本源有助于对抗 XSS。
有关 Web 应用程序安全性的一般 HTTP 响应标头,请查看 OWASP: https://www.owasp.org/index.php/List_of_useful_HTTP_headers
评论
在 PHP 中使用 htmlspecialchars()。在 HTML 中,尽量避免使用:
element.innerHTML = “…”; element.outerHTML = “…”; document.write(…); document.writeln(…);
where 由用户控制。var
显然也尽量避免.
如果你必须使用它们中的任何一个,那么请尝试 JavaScript 转义它们,HTML 转义它们,你可能需要做更多的事情,但对于基础知识来说,这应该足够了。eval(var)
我将其作为即将离线的 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 参数包含,则 PHP 脚本的输出将为:<script src="http://example.com/runme.js"></script>
<div><script src="http://example.com/runme.js"></script></div>
第三方 JavaScript 代码将运行,用户将在网页上看到“我正在运行”。
溶液
作为一般规则,永远不要相信来自客户端的输入。每个 GET 参数、POST 或 PUT 内容以及 cookie 值都可以是任何值,因此应进行验证。输出这些值中的任何一个时,请对它们进行转义,这样它们就不会以意外的方式被评估。
请记住,即使在最简单的应用程序中,数据也可以四处移动,并且很难跟踪所有来源。因此,最佳做法是始终对输出进行转义。
PHP 提供了几种根据上下文转义输出的方法。
筛选函数
PHP 的过滤函数允许以多种方式清理或验证 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><script src="http://example.com/runme.js"></script></div>
标签内的所有内容都不会被浏览器解释为 JavaScript 标签,而是被解释为一个简单的文本节点。用户将安全地看到:<div>
<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 Purifier。
评论
许多框架以各种方式帮助处理 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;
以上将删除所有 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);
}
评论
保护 HTML 输出的最佳方法是使用 htmlentities() 函数。
例:
htmlentities($target, ENT_QUOTES, 'UTF-8');
评论
href
src