opctim/symfony-csp-bundle

这个包帮助您在symfony应用中使用CSP头部来正确地保护您的应用程序。

安装次数: 215

依赖者: 0

建议者: 0

安全: 0

星星: 1

关注者: 1

分支: 0

开放性问题: 0

类型:symfony-bundle

1.1.4 2024-05-06 14:10 UTC

This package is auto-updated.

Last update: 2024-09-06 14:48:23 UTC


README

Latest Stable Version Total Downloads Latest Unstable Version License PHP Version Require

曾经与CSP头部抗争过吗?我也是。以前配置CSP头部总是很痛苦。

但设置CSP头部指令比以往任何时候都更重要!如果您曾经遇到过不同的跟踪脚本,您可能也注意到了有多少额外的第三方脚本被懒加载。这可能会导致恶意JavaScript被加载到您的页面上,这可能会造成灾难性的后果,尤其是在构建支付网关时。

它甚至可以帮助您在unsafe-inline指令(您应该避免使用)的情况下添加动态的Nonce标记

要求

  • PHP >= 7.4.33,已安装OpenSSL扩展
  • Symfony >= 5.4

安装

composer require opctim/symfony-csp-bundle

配置

在您的config/目录中,添加或编辑opctim_csp_bundle.yaml

# config/packages/opctim_csp_bundle.yaml

opctim_csp_bundle:
    
    always_add: []
    
    report:
        url: null
        route: null
        route_params: []
        chance: 100

    directives:
        default-src:
            - "'self'"
            - 'data:'
            - '*.example.com'
        base-uri:
            - "'self'"
        object-src:
            - "'none'"
        script-src:
            - "'self'"
            - "nonce(payment-app)" # For more info, see "Dynamic nonce tokens" section below!
            - '*.example.com'
        img-src:
            - "'self'"
            - '*.example.com'
        style-src:
            - "'self'"
            - "'unsafe-inline'"
        connect-src:
            - '*.example.com'
        font-src:
            - '*.example.com'
        frame-src:
            - "'self'"
            - '*.example.com'
        frame-ancestors:
            - "'self'"
            - '*.example.com'

您可以使用任何指令!这只是一个编写指令的华丽方式。

所以

default-src:
    - "'self'"
    - 'data:'
    - '*.example.com'

变成了

Content-Security-Policy: default-src 'self' data: *.example.com;

always_add选项

如名称所示,此选项将指定的来源添加到所有指令中。这对于与when@dev一起使用非常有用。

# config/packages/opctim_csp_bundle.yaml

opctim_csp_bundle:
    always_add: []
    
    directives:
        default-src:
            - "'self'"
            - 'data:'
            - '*.example.com'
        base-uri:
            - "'self'"
        object-src:
            - "'none'"
        script-src:
            - "'self'"
            - "nonce(payment-app)"  # For more info, see "Dynamic nonce tokens" section below!
            - '*.example.com'
    
when@dev: 
    opctim_csp_bundle:
        always_add:
            - '*.example.local'

重要:如果您将'none'作为第一个和唯一的指令添加,则此指令将跳过always_add功能。此功能是在1.1.4中添加的。

您还可以使用when@dev有条件地向特定指令添加来源。

# config/packages/opctim_csp_bundle.yaml

opctim_csp_bundle:
    always_add: []
    
    directives:
        default-src:
            - "'self'"
            - 'data:'
            - '*.example.com'
        script-src:
            - "'self'"
            - '*.example.com'
    
when@dev:
    opctim_csp_bundle:
        directives:  
            connect-src:
                - 'some.external.additional.host.com'

报告选项

此包为您提供了一种简单配置CSP报告功能的方法,该功能告诉浏览器如果您的CSP配置拒绝特定资源,则向您的后端报告。浏览器中目前有两种实现 - report-uri和report-to

因此,根据MDN文档,此包添加了report-uri指令和Reporting-Endpoint头部,以支持未来的新浏览器。

此包提供了一个向后兼容的实现,应该被所有浏览器支持。

# config/packages/opctim_csp_bundle.yaml

opctim_csp_bundle:
    always_add: []
    
    report:
        url: null
        route: my_awesome_controller_action
        route_params: []
        chance: 100

    directives:
        default-src:
            - "'self'"
            - 'data:'
            - '*.example.com'
  • url - 可选您可以将外部URL传递到这里,浏览器应该向其报告。
  • route - 可选如果您想使用您的控制器动作接收报告。这将使用UrlGenerator为您生成一个绝对URL。
  • route_params - 可选如果您正在使用route参数,您可以在其中传递额外的路由参数。
  • chance - 可选此字段的单位是百分比。它指定了将报告指令添加到响应中的概率应该有多高。

以下是一些解释更改选项的伪代码

if (random_int(0, 99) < $chance) {
    $someService->addReportHeaders();
}

这意味着,对于100%的概率,它将每次都运行。根据您应用程序的流量,建议设置大约5-10%的概率,以避免被CSP日志消息淹没。

动态nonce标记

动态nonce标记可以非常有用,允许在您的Twig模板中允许特定的内联脚本标记,而无需忽略安全担忧,例如通过不添加或硬编码它们;)

配置语法

nonce(<handle>)

示例

opctim_csp_bundle.yaml

opctim_csp_bundle:
    always_add: []
    
    directives:
        default-src:
            - "'self'"
            - 'data:'
            - '*.example.com'
        script-src:
            - "'self'"
            - '*.example.com'
            - 'nonce(my-inline-script)' 

根据请求,nonce(my-inline-script) 将被转换为例如 nonce-25d2ec8bb6,并随后出现在响应的CSP头部中。

然后,在您的twig模板中,您可以简单地使用此bundle提供的 csp_nonce('my-inline-script') 函数。

<script type="text/javascript" nonce="{{ csp_nonce('my-inline-script') }}">
    alert('This works!');
</script>

渲染结果

<script type="text/javascript" nonce="25d2ec8bb6">
    alert('This works!');
</script>

挂钩到CSP头部生成

此bundle的一个关键特性是动态nonce实现。bundle挂钩到Symfony事件系统,并为您生成新的nonce令牌 - 每个请求都会生成!

在请求时,bundle准备将CSP头部指令写入响应头。在这里,解析了来自 opctim_csp_bundle.yamlnonce() 表达式。

bundle会将此值添加到以下三个头部中,以实现跨浏览器的兼容性

  • 内容安全策略
  • X-内容安全策略
  • X-WebKit-CSP

如果您想在写入响应之前修改CSP头部,可以通过订阅 opctim_csp_bundle.add_csp_header 事件来挂钩到生成过程。

<?php # src/EventSubscriber/ModifyCspHeaderEventSubscriber.php

declare(strict_types=1);

namespace App\EventSubscriber;

use Opctim\CspBundle\Event\AddCspHeaderEvent;
use Opctim\CspBundle\Service\CspHeaderBuilderService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ModifyCspHeaderEventSubscriber implements EventSubscriberInterface 
{
    public function __construct(
        private CspHeaderBuilderService $cspHeaderBuilderService
    ) 
    {}   

    public static function getSubscribedEvents(): array
    {
        return [
            AddCspHeaderEvent::NAME => 'modifyCspHeader'
        ];
    }
    
    public function modifyCspHeader(AddCspHeaderEvent $event): void
    {
        // Use the request if you like
        $request = $event->getRequest();
    
        $cspHeader = $this->cspHeaderBuilderService->build(
            [ // alwaysAdd options
                ...$this->cspHeaderBuilderService->getAlwaysAdd(), // Merge the existing ones...
                'some-conditional-always-to-be-added-origin'
            ], 
            [ // directive options
                ...$this->cspHeaderBuilderService->getDirectives(), // Merge the existing ones...
                'script-src' => [ // Override something here
                    'some-conditional-origin'
                ]
            ]
        );
        
        $cspHeader = str_replace('foo', 'bar', $cspHeader); // Maybe some string transformations here...
        
        $event->setCspHeaderValue($cspHeader); // Set the newly crafted csp header.
        
        // On response, the bundle will add your new CSP header!
    }
}

测试

测试位于 tests/ 文件夹中,可以使用 vendor/bin/phpunit 运行。

composer install

vendor/bin/phpunit