内容安全策略

什么是内容安全策略?

您抵御 XSS 攻击的最佳保护措施之一是在网站上实施内容安全策略 (CSP)。这要求您指定和授权包含在网站 HTML 中的每个内容来源,包括图像、样式表、JavaScript 文件等。浏览器将拒绝来自未明确批准的来源的内容。此授权是在响应的 Content-Security-Policy 标头中定义的,并提供各种配置选项。

这听起来很复杂,而且在某些网站上,确实可能很困难。但是,对于许多简单的网站来说,所有内容都由同一个域提供服务(例如,http://example.com),集成起来非常简单。

由于这是一个复杂主题,本用户指南不会介绍所有细节。有关更多信息,您应该访问以下网站

启用 CSP

重要

调试工具可能使用 Kint,它输出内联脚本。因此,当启用 CSP 时,CSP nonce 会自动为调试工具输出。但是,如果您没有使用 CSP nonce,这将更改 CSP 标头,使其与您预期不同,并且其行为将与生产环境中不同;如果您想验证 CSP 行为,请关闭调试工具。

默认情况下,对它的支持是关闭的。要在您的应用程序中启用支持,请编辑 **app/Config/App.php** 中的 CSPEnabled 值。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class App extends BaseConfig
{
    // ...

    public bool $CSPEnabled = true;
}

启用后,响应对象将包含 CodeIgniter\HTTP\ContentSecurityPolicy 的实例。**app/Config/ContentSecurityPolicy.php** 中设置的值将应用于该实例,如果在运行时不需要进行任何更改,则会发送格式正确的标头,您就完成了。

启用 CSP 后,HTTP 响应中会添加两行标头:一个 **Content-Security-Policy** 标头,其中包含策略,用于识别针对不同上下文明确允许的内容类型或来源,以及一个 **Content-Security-Policy-Report-Only** 标头,它识别将被允许但也会被报告到您选择的目的地的内容类型或来源。

我们的实现提供了一种默认处理方式,可以通过 reportOnly() 方法进行更改。当向 CSP 指令添加额外的条目时,如下所示,它将被添加到适合阻止或阻止的 CSP 标头中。可以通过向添加方法调用提供可选的第二个参数来覆盖它。

运行时配置

如果您的应用程序需要在运行时进行更改,您可以在控制器中访问 $this->response->getCSP() 处的实例。该类包含许多方法,这些方法与您需要设置的适当标头值非常清楚地对应。下面显示了示例,其中包含不同的参数组合,但所有参数都接受指令名称或指令数组。

<?php

// get the CSP instance
$csp = $this->response->getCSP();

// specify the default directive treatment
$csp->reportOnly(false);

// specify the origin to use if none provided for a directive
$csp->setDefaultSrc('cdn.example.com');

// specify the URL that "report-only" reports get sent to
$csp->setReportURI('http://example.com/csp/reports');

// specify that HTTP requests be upgraded to HTTPS
$csp->upgradeInsecureRequests(true);

// add types or origins to CSP directives
// assuming that the default treatment is to block rather than just report
$csp->addBaseURI('example.com', true); // report only
$csp->addChildSrc('https://youtube.com'); // blocked
$csp->addConnectSrc('https://*.facebook.com', false); // blocked
$csp->addFontSrc('fonts.example.com');
$csp->addFormAction('self');
$csp->addFrameAncestor('none', true); // report this one
$csp->addImageSrc('cdn.example.com');
$csp->addMediaSrc('cdn.example.com');
$csp->addManifestSrc('cdn.example.com');
$csp->addObjectSrc('cdn.example.com', false); // reject from here
$csp->addPluginType('application/pdf', false); // reject this media type
$csp->addScriptSrc('scripts.example.com', true); // allow but report requests from here
$csp->addStyleSrc('css.example.com');
$csp->addSandbox(['allow-forms', 'allow-scripts']);

每个“add”方法的第一个参数都是一个合适的字符串值,或者是一个字符串数组。

reportOnly() 方法允许您为后续的源指定默认的报告处理方式,除非被覆盖。例如,您可以指定 youtube.com 是允许的,然后提供几个允许但需要报告的源。

<?php

// get the CSP instance
$csp = $this->response->getCSP();

$csp->addChildSrc('https://youtube.com'); // allowed
$csp->reportOnly(true);
$csp->addChildSrc('https://metube.com'); // allowed but reported
$csp->addChildSrc('https://ourtube.com', false); // allowed

内联内容

可以将网站设置为不保护其自身页面上的内联脚本和样式,因为这可能是用户生成内容的结果。为了防止这种情况,CSP 允许您在 <style><script> 标签中指定一个 nonce,并将这些值添加到响应的标头中。

使用占位符

在现实生活中处理这个问题很麻烦,而且在动态生成时最安全。为了简化操作,您可以在标签中包含 {csp-style-nonce}{csp-script-nonce} 占位符,它将自动为您处理。

// Original
<script {csp-script-nonce}>
    console.log("Script won't run as it doesn't contain a nonce attribute");
</script>

// Becomes
<script nonce="Eskdikejidojdk978Ad8jf">
    console.log("Script won't run as it doesn't contain a nonce attribute");
</script>

// OR
<style {csp-style-nonce}>
    . . .
</style>

警告

如果攻击者注入一个像 <script {csp-script-nonce}> 这样的字符串,它可能会成为使用此功能的真实 nonce 属性。您可以使用 app/Config/ContentSecurityPolicy.php 中的 $scriptNonceTag$styleNonceTag 属性自定义占位符字符串。

使用函数

如果您不喜欢上面的自动替换功能,可以通过在 app/Config/ContentSecurityPolicy.php 中设置 $autoNonce = false 来关闭它。

在这种情况下,您可以使用函数 csp_script_nonce()csp_style_nonce()

// Original
<script <?= csp_script_nonce() ?>>
    console.log("Script won't run as it doesn't contain a nonce attribute");
</script>

// Becomes
<script nonce="Eskdikejidojdk978Ad8jf">
    console.log("Script won't run as it doesn't contain a nonce attribute");
</script>

// OR
<style <?= csp_style_nonce() ?>>
    . . .
</style>