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

我已经看到过许多其他有关此主题的文章,但是我还没有找到一篇文章清楚,简洁地说明如何实际防止XSS。

评论

请注意,这不能解决您可能希望将用户输入用作HTML属性的情况。例如,图像的源URL。这不是常见的情况,但很容易忘记。

@MichaelMior这是防止在href或src HTML属性中使用XSS的解决方案:stackoverflow.com/questions/19047119/…

这里有一篇不错的文章,它解释了XSS以及如何用不同的语言(包括PHP)防止它。

#1 楼

基本上,每当您想将来自用户输入的内容输出到浏览器时,都需要使用功能htmlspecialchars()

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

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


Google Code University也提供了有关Web安全性的以下非常有教育意义的视频:


如何破解Web软件-看看安全性漏洞
/>网络软件
每个工程师需要了解的安全性
以及在何处学习


评论


@TimTim:是的,在大多数情况下。但是,当您需要允许HTML输入时,事情会变得有些棘手,如果是这种情况,我建议您使用htmlpurifier.org之类的东西。

– Alix Axel
2010年1月3日于20:23

@Alix Axel,您的答案是使用htmlspecialchars还是使用htmlpurifier.org?

– TimTim
10年3月3日在20:39

如果需要接受HTML输入,请使用HTML Purifier,如果不需要,请使用htmlspecialchars()。

– Alix Axel
2010年1月3日于20:41

htmlspecialchars或htmlentities?在这里检查stackoverflow.com/questions/46483/…

– Kiranvj
2012年11月16日下午6:19

大多数时候,它是正确的,但事实并非如此简单。您应该考虑将不受信任的字符串放入HTML,Js,Css,并考虑将不受信任的HTML放入HTML。看这个:owasp.org/index.php/…

–青铜人
2014年5月29日17:43

#2 楼

最重要的步骤之一是在处理和/或呈现回浏览器之前清除所有用户输入。 PHP具有一些可以使用的“过滤器”功能。

XSS攻击通常具有的形式是插入指向包含用户恶意意图的非现场javascript的链接。在此处了解有关此内容的更多信息。

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

评论


我需要确保从输入中准确清除输入内容。我需要注意一个特定的字符/字符串吗?

– TimTim
10年3月3日在20:14

@TimTim-不所有用户输入始终应被视为天生具有敌意。

– zombat
10年3月3日在20:28

此外,内部数据(员工,系统管理员等)可能是不安全的。您应该识别并监视(带有日志日期和用户)解释显示的数据。

–塞缪尔·道祖(Samuel Dauzon)
18-10-4在8:40

#3 楼

按优先顺序排列:


如果使用模板引擎(例如Twig,Smarty,Blade),请检查其是否提供上下文相关的转义。我从经验中知道,小枝确实。 {{ var|e('html_attr') }}

如果要允许HTML,请使用HTML Purifier。即使您认为您只接受Markdown或ReStructuredText,您仍然希望清除这些标记语言输出的HTML。否则,请使用htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)并确保文档的其余部分使用与$charset相同的字符集。在大多数情况下,'UTF-8'是所需的字符集。

此外,请确保您在输出而不是输入上转义。

#4 楼

将其交叉发布以作为即将脱机的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或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 Purifier

#5 楼

许多框架以各种方式帮助处理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);
}


#6 楼

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


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


确保已启用浏览器XSS保护模式。


Content-Security-Policy“ 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

#7 楼

<?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', ';', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', ';', $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', '>', $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', '=nojavascript...', $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', '=novbscript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '=nomozbinding...', $data);

// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '>', $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', '>', $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。 owasp.org/index.php/PHP_Security_Cheat_Sheet#Code_Injection

–螃蟹实验室
17 Mar 11 '17 at 17:19

#8 楼

htmlspecialchars上使用PHP。在HTML上,请尝试避免使用:

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

其中var由用户控制。

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

#9 楼

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

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


您可以在此处获取更多信息。