如何仅使用HTML和PHP防止XSS(跨站点脚本)?
我已经看过许多其他有关该主题的文章,但是我还没有找到一篇文章清楚,简洁地说明如何实际防止XSS。
如何仅使用HTML和PHP防止XSS(跨站点脚本)?
我已经看过许多其他有关该主题的文章,但是我还没有找到一篇文章清楚,简洁地说明如何实际防止XSS。
Answers:
基本上,htmlspecialchars()
每当您想要将来自用户输入的内容输出到浏览器时,都需要使用该函数。
使用此功能的正确方法如下所示:
echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
Google Code University在网络安全方面也提供了以下非常有教育意义的视频:
htmlspecialchars()
。
我最喜欢的OWASP参考之一是Cross-Site Scripting解释,因为尽管有大量的XSS攻击媒介,但遵循以下一些规则可以极大地抵御大多数攻击!
这是PHP安全备忘单
最重要的步骤之一是在处理和/或呈现回浏览器之前清除所有用户输入。PHP具有一些可以使用的“ 过滤器 ”功能。
XSS攻击通常具有的形式是插入指向包含用户恶意意图的非现场javascript的链接。在此处了解更多信息。
您还需要测试您的网站-我可以推荐Firefox附加组件XSS Me。
按优先顺序:
{{ var|e('html_attr') }}
htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)
并确保文档的其余部分使用与相同的字符集$charset
。在大多数情况下,'UTF-8'
是所需的字符集。另外,请确保在输出而不是input上进行转义。
将其交叉发布,以作为即将下线的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提供了几种根据上下文对输出进行转义的方法。
PHP过滤器功能允许以多种方式对php脚本的输入数据进行清理或验证。当保存或输出客户端输入时,它们很有用。
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>
<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参数。
有时您会想要发送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);
}
您还可以通过以下方式设置一些与XSS相关的HTTP响应标头 header(...)
X-XSS-Protection“ 1; mode = block”
确保已启用浏览器XSS保护模式。
内容安全策略“ default-src'self'; ...”
以实现浏览器端内容安全。有关内容安全策略(CSP)的详细信息,请参阅此文档:http : //content-security-policy.com/ 特别是设置CSP来阻止内联脚本和外部脚本源对于XSS很有帮助。
有关与您的Web应用程序的安全性有关的一堆有用的HTTP响应标头,请查看OWASP:https : //www.owasp.org/index.php/List_of_useful_HTTP_headers
<?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;
}