spaze/csp-config

从配置文件构建内容安全策略

v4.0.1 2023-10-28 14:01 UTC

This package is auto-updated.

Last update: 2024-08-28 16:10:30 UTC


README

从配置文件构建内容安全策略。支持按页面或模块设置不同的策略,并在需要时可以动态添加片段。

PHP Tests

此库旨在与任何框架(或无需框架)一起使用,但附带一个Nette 框架的桥梁。

请注意,此库仅构建头部值,您仍然需要自己发送头部!

安装

安装库的最佳方式是使用 Composer

composer require spaze/csp-config

Nette 框架配置

如果您使用 Nette 框架,可以将扩展添加到您的配置文件中

extensions:
    contentSecurityPolicy: Spaze\ContentSecurityPolicy\Bridges\Nette\CspConfigExtension

示例配置

这是一个示例配置,它在这里是为了解释事物,并且故意不完整。您还可以查看我网站使用的配置

contentSecurityPolicy:
    snippets:
        slideshare:
            child-src:
                - https://www.slideshare.net
    policies:
        *.*:
            default-src: "'none'"
            form-action: "'none'"
            report-uri: https://report-uri.com.example.net
            report-to: default
        www.*.*:
            default-src: "'none'"
            script-src:
                - "'strict-dynamic'"
                - "'nonce'"
                - "'self'"
                - "'report-sample'"
            upgrade-insecure-requests:
        www.trainings.training:
            @extends: www.*.*
            connect-src: https://api.example.com
        admin.*.*:
            @extends: www.*.*
        admin.blog.add:
            @extends: admin.*.*
            connect-src: "'self'"
        admin.blog.edit:
            @extends: admin.blog.add
    policiesReportOnly:
      *.*:
        default-src: "'self'"

让我们解释

  • snippets 这里定义您的片段。一个片段由一个或多个内容安全策略指令组成,可以像这样通过 addSnippet(string $snippetName) 方法添加到当前的内容安全策略头部:$this->contentSecurityPolicy->addSnippet($type); 您可以使用它来在页面上有视频时扩展策略。在 snippets.neon 中有示例片段,如果您想的话,可以直接将其包含在配置中。

  • policies 您的内容安全策略在这里。下面的键意味着 [module.]presenter.action,支持通配符。

    • *.* 表示 对所有演示者和动作使用这些。如上例所示,我使用了相当严格的政策,稍后会允许更多。
    • www.*.* 适用于 "www" 模块中的所有演示者和动作。
    • @extends: www.*.* 此配置扩展 www.*.* 配置,指定的任何值都将添加或合并。您可以使用它来扩展某些页面或动作的默认策略。您可以通过在指令名称前加 ! 来禁用合并,从而有效地覆盖扩展的值,请见下文
  • policiesReportOnlypolicies 但旨在与 Content-Security-Policy-Report-Only 头部一起使用,请见下文。

策略可以包含一些特殊的键和值

  • 没有值的键,如上例中的 upgrade-insecure-requests:,将使策略头部仅包含键名称而没有值
  • 'nonce' 将向头部添加一个 CSP nonce ('nonce-somethingrandomandunique')。Nonces 在 CSP2 中定义,并在建议的策略中使用 CSP3 'strict-dynamic'。为此需要 spaze/nonce-generator。它还将返回不可变的 nonce,以便您可以将其添加到您的 <script> 标签中。这可以通过 spaze/sri-macros 自动完成。

覆盖值

如果您不想将扩展值与原始值合并,请在配置中的指令名称前加上感叹号(!)。以下是一个简单的示例配置

contentSecurityPolicy:
    policies:
        *.*:
            default-src: "'none'"
        www.*:
            @extends: *.*
            default-src: "'self'"

调用 getHeader('www:...') 将返回 default-src 'none' 'self',这没有意义,而且 'none' 甚至会被忽略。

将配置更改为如下(注意 default-src 中的 ! 前缀)

contentSecurityPolicy:
    policies:
        *.*:
            default-src: "'none'"
        www.*:
            @extends: *.*
            !default-src: "'self'"

然后调用 getHeader('www:...') 将返回 default-src 'self',这可能是您想要的。

如何在 Nette 框架中发送生成的头部信息

$header = $this->contentSecurityPolicy->getHeader($presenter->getAction(true));
if ($header) {
    $this->httpResponse->setHeader('Content-Security-Policy', $header);
}

例如,您可以通过以下方式从 \Nette\Application\Application 获取 $presenter

/** @var \Nette\Application\UI\Presenter $presenter */
$presenter = $this->application->getPresenter();
$actionName = $presenter->getAction(true);

并从依赖注入容器中获取 $this->application

public function __construct(private \Nette\Application\Application $application)
{
}

如果您在一个演示者(presenter)中,可以使用 $this->getAction(true) 代替。

仅报告策略

使用 policiesReportOnly 配置键来定义与 Content-Security-Policy-Report-Only 头部一起使用的策略

contentSecurityPolicy:
    policies:
        *.*:
            default-src: "'none'"
    policiesReportOnly:
        *.*:
            default-src: "'self'"

通过调用 getHeaderReportOnly() 方法获取策略

$header = $this->contentSecurityPolicy->getHeaderReportOnly($presenter->getAction(true));
if ($header) {
    $this->httpResponse->setHeader('Content-Security-Policy-Report-Only', $header);
}

您可以发送 强制执行仅报告 策略,这在策略升级等情况下非常有用

$header = $this->contentSecurityPolicy->getHeader($presenter->getAction(true));
if ($header) {
    $this->httpResponse->setHeader('Content-Security-Policy', $header);
}
$header = $this->contentSecurityPolicy->getHeaderReportOnly($presenter->getAction(true));
if ($header) {
    $this->httpResponse->setHeader('Content-Security-Policy-Report-Only', $header);
}