PHP 跨站脚本(XSS)

示例

问题

跨站点脚本是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 xx_src="http://example.com/runme.js"></script></div>

第三方JavaScript将运行,并且用户将在网页上看到“我正在运行”。

通常,永远不要信任来自客户端的输入。每个GET,POST和cookie值都可以是任何值,因此应进行验证。输出这些值中的任何一个时,请对其进行转义,以便不会以意外的方式对其进行求值。

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

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

过滤功能

PHP过滤器功能允许以多种方式对php脚本的输入数据进行清理或验证。当保存或输出客户端输入时,它们很有用。

HTML编码

htmlspecialchars会将所有“ HTML特殊字符”转换为它们的HTML编码,这意味着它们将被视为标准HTML。要使用此方法修复前面的示例:

<?php
echo '<div>' . htmlspecialchars($_GET['input']) . '</div>';
// 要么
echo '<div>' . filter_input(INPUT_GET, 'input', FILTER_SANITIZE_SPECIAL_CHARS) . '</div>';

将输出:

<div>&lt;script xx_src=&quot;http://example.com/runme.js&quot;&gt;&lt;/script&gt;</div>

<div>标签内部的所有内容都不会被浏览器解释为JavaScript标签,而是解释为简单的文本节点。用户将安全地看到:

<script xx_src="http://example.com/runme.js"></script>

URL编码

输出动态生成的URL时,PHP提供了urlencode安全输出有效URL的功能。因此,例如,如果用户能够输入成为另一个GET参数一部分的数据:

<?php
$input = urlencode($_GET['input']);
// 要么
$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