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

第三方 JavaScript 将运行,用户将在网页上看到我正在运行

作为一般规则,永远不要相信来自客户的输入。每个 GET,POST 和 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 时,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