offline/laravel-csp

为 Laravel 应用的响应添加 CSP 头部 (Fork)

v4.0.0 2022-05-04 14:40 UTC

README

Latest Version on Packagist GitHub Workflow Status StyleCI Quality Score Total Downloads

默认情况下,网页上的所有脚本都被允许向任何网站发送和获取数据。这可能会导致安全问题。想象一下,你的 JavaScript 依赖项将所有按键,包括密码,发送到第三方网站。

有人很容易隐藏这种恶意行为,让你几乎无法检测到它(除非你手动阅读你网站上所有的 JavaScript 代码)。为了更好地了解为什么你需要设置内容安全策略头部,请阅读 David Gilbertson 的这篇优秀的博客文章:https://hackernoon.com/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8cb347c5b5

设置内容安全策略头部有助于解决这个问题。这些头部指定了哪些网站可以与你的网站进行通信。这个包让你可以轻松地设置正确的头部。

本说明文档的目的不是全面解释 CSP 和其指令的所有可能用法。我们强烈建议你在使用此包之前阅读 Mozilla 的内容安全策略文档

如果你是一个视听学习者,你应该看看这个关于如何使用此包的视频。 https://www.laraning.com/videos/OFFLINE-csp-content-security-policy

支持我们

我们投入了大量资源来创建 一流的开放源代码包。你可以通过 购买我们的付费产品之一 来支持我们。

我们非常感谢你从家乡寄给我们明信片,提到你正在使用我们哪些包。你可以在 我们的联系页面 上找到我们的地址。我们将发布所有收到的明信片在 我们的虚拟明信片墙上

安装

您可以通过 composer 安装此包

composer require OFFLINE/laravel-csp

您可以使用以下命令发布配置文件

php artisan vendor:publish --provider="OFFLINE\LaravelCSP\CspServiceProvider" --tag="config"

此文件的内容将发布在 config/csp.php

return [

    /*
     * A policy will determine which CSP headers will be set. A valid CSP policy is
     * any class that extends `OFFLINE\LaravelCSP\Policies\Policy`
     */
    'policy' => OFFLINE\LaravelCSP\Policies\Basic::class,

    /*
     * This policy which will be put in report only mode. This is great for testing out
     * a new policy or changes to existing csp policy without breaking anything.
     */
    'report_only_policy' => '',

    /*
     * All violations against the policy will be reported to this url.
     * A great service you could use for this is https://report-uri.com/
     *
     * You can override this setting by calling `reportTo` on your policy.
     */
    'report_uri' => env('CSP_REPORT_URI', ''),

    /*
     * Headers will only be added if this setting is set to true.
     */
    'enabled' => env('CSP_ENABLED', true),

    /*
     * The class responsible for generating the nonces used in inline tags and headers.
     */
    'nonce_generator' => OFFLINE\LaravelCSP\Nonce\RandomString::class,
];

您可以通过在 http kernel 中注册 OFFLINE\LaravelCSP\AddCspHeaders::class 来将 CSP 头部添加到您应用程序的所有响应中。

// app/Http/Kernel.php

...

protected $middlewareGroups = [
   'web' => [
       ...
       \OFFLINE\LaravelCSP\AddCspHeaders::class,
   ],

或者,您也可以在路由或路由组级别应用中间件。

// in a routes file
Route::get('my-page', 'MyController')->middleware(OFFLINE\LaravelCSP\AddCspHeaders::class);

您还可以将策略类作为参数传递给中间件

// in a routes file
Route::get('my-page', 'MyController')->middleware(OFFLINE\LaravelCSP\AddCspHeaders::class . ':' . MyPolicy::class);

给定的策略将覆盖配置文件中为该特定路由或路由组配置的策略。

用法

此包允许您定义 CSP 策略。CSP 策略决定了将在响应头中设置的哪些 CSP 指令。

CSP 指令的一个例子是 script-src。如果此处的值为 'self' www.google.com,则您的网站只能从它自己的域名或 www.google.com 加载脚本。您可以在 Mozilla 优秀的开发者网站上找到 所有 CSP 指令的列表

根据规范,某些指令值需要用引号包围。例如,有 'self''none''unsafe-inline'。当使用 addDirective 函数时,您不需要手动用引号包围指令值。我们将自动添加引号。脚本/样式哈希也将自动检测并用引号包围。

// in a policy
...
   ->addDirective(Directive::SCRIPT, Keyword::SELF) // will output `'self'` when outputting headers
   ->addDirective(Directive::STYLE, 'sha256-hash') // will output `'sha256-hash'` when outputting headers
...

您可以在同一个指令中添加多个策略选项,将数组作为 addDirective 的第二个参数,或者将每个选项通过一个或多个空格分隔的单个字符串。

// in a policy
...
   ->addDirective(Directive::SCRIPT, [
       Keyword::STRICT_DYNAMIC,
       Keyword::SELF,
       'www.google.com',
   ])
   ->addDirective(Directive::SCRIPT, 'strict-dynamic self  www.google.com')
   // will both output `'strict_dynamic' 'self' www.google.com` when outputting headers
...

也有一些情况下,您不需要或不需要指定值,例如 upgrade-insecure-requests、block-all-mixed-content 等。在这种情况下,您可以使用以下值

// in a policy
...
    ->addDirective(Directive::UPGRADE_INSECURE_REQUESTS, Value::NO_VALUE)
    ->addDirective(Directive::BLOCK_ALL_MIXED_CONTENT, Value::NO_VALUE);
...

这将输出类似以下内容的 CSP

Content-Security-Policy: upgrade-insecure-requests;block-all-mixed-content

创建策略

csp 配置文件的 policy 键默认设置为 \OFFLINE\LaravelCSP\Policies\Basic::class。这个类允许您的网站仅使用自己的图像、脚本和表单操作。这个类看起来是这样的。

namespace OFFLINE\LaravelCSP\Policies;

use OFFLINE\LaravelCSP\Directive;
use OFFLINE\LaravelCSP\Value;

class Basic extends Policy
{
    public function configure()
    {
        $this
            ->addDirective(Directive::BASE, Keyword::SELF)
            ->addDirective(Directive::CONNECT, Keyword::SELF)
            ->addDirective(Directive::DEFAULT, Keyword::SELF)
            ->addDirective(Directive::FORM_ACTION, Keyword::SELF)
            ->addDirective(Directive::IMG, Keyword::SELF)
            ->addDirective(Directive::MEDIA, Keyword::SELF)
            ->addDirective(Directive::OBJECT, Keyword::NONE)
            ->addDirective(Directive::SCRIPT, Keyword::SELF)
            ->addDirective(Directive::STYLE, Keyword::SELF)
            ->addNonceForDirective(Directive::SCRIPT)
            ->addNonceForDirective(Directive::STYLE);
    }
}

您可以通过扩展这个类来允许从 www.google.com 获取脚本

namespace App\Services\Csp\Policies;

use OFFLINE\LaravelCSP\Directive;
use OFFLINE\LaravelCSP\Policies\Basic;

class MyCustomPolicy extends Basic
{
    public function configure()
    {
        parent::configure();
        
        $this->addDirective(Directive::SCRIPT, 'www.google.com');
    }
}

别忘了在 csp 配置文件中将 policy 键设置为策略类的名称(在这种情况下将是 App\Services\Csp\Policies\MyCustomPolicy)。

使用内联脚本和样式

当使用 CSP 时,您必须明确允许使用内联脚本或样式。使用此包推荐的方法是使用一个 nonce。nonce 是每个请求都是唯一的数字。nonce 必须在 CSP 标题和 HTML 标签上的属性中指定。这样,攻击者就没有注入恶意脚本或样式的途径。

首先,您必须将 nonce 添加到策略中的正确指令

// in a policy

public function configure()
  {
      $this
        ->addDirective(Directive::SCRIPT, 'self')
        ->addDirective(Directive::STYLE, 'self')
        ->addNonceForDirective(Directive::SCRIPT)
        ->addNonceForDirective(Directive::STYLE)
        ...
}

接下来,您必须将 nonce 添加到 HTML 中

{{-- in a view --}}
<style nonce="{{ csp_nonce() }}">
   ...
</style>

<script nonce="{{ csp_nonce() }}">
   ...
</script>

还有其他一些选项可以用于内联样式和脚本。请参阅 Mozilla 开发者网站上关于 CSP 的文档 了解更多。

报告 CSP 错误

在浏览器中

您可以将策略设置为仅报告模式,而不是直接阻止所有违规行为。在这种情况下,所有请求都将被发出,但所有违规行为都将显示在您喜欢的浏览器的控制台中。

要将策略设置为仅报告模式,只需在报告的 configure() 函数中调用 reportOnly()

public function configure()
{
    parent::configure();
    
    $this->reportOnly();
}

到外部 URL

任何违反策略的行为都可以报告给指定的 URL。您可以在 csp 配置文件的 report_uri 键中设置该 URL。一个专为处理这些违规报告而构建的出色服务是 http://report-uri.io/

使用多个策略

要测试对您的 CSP 策略的更改,您可以在 csp 配置键的 report_only_policy 中指定第二个策略。指定的 policy 将被强制执行,而 report_only_policy 中的策略则不会。这对于在不破坏任何内容的情况下测试新策略或现有 CSP 策略的更改非常有用。

使用 whoops

Laravel 附带 whoops,这是一个错误处理框架,它通过异常的精美可视化帮助您调试应用程序。Whoops 使用内联脚本和样式,因为它无法对其被使用的环境做出任何假设,因此除非您为脚本和样式允许 unsafe-inline,否则它将不起作用。

解决此问题的一个方法是在设置策略时检查config('app.debug')。遗憾的是,这存在忘记在所有CSP规则启用的情况下测试你的代码,导致应用程序在部署时崩溃的风险。作为替代方案,你可以在异常处理器的render方法中添加以下内容,仅将unsafe-inline允许在错误页面上:

$this->container->singleton(AppPolicy::class, function ($app) {
    return new AppPolicy();
});
app(AppPolicy::class)->addDirective(Directive::SCRIPT, Keyword::UNSAFE_INLINE);
app(AppPolicy::class)->addDirective(Directive::STYLE, Keyword::UNSAFE_INLINE);

其中AppPolicy是您的CSP策略名称。在所有其他情况下,这也可以在运行时更改策略,在这种情况下,应该在服务提供程序而不是异常处理器中执行单例注册。

请注意,unsafe-inline仅在您没有发送nonce或strict-dynamic指令时才有效,因此要能够使用此解决方案,您必须在CSP标题中指定所有内联脚本和样式的哈希值。

测试

您可以使用以下命令运行所有测试:

composer test

变更日志

有关最近更改的更多信息,请参阅变更日志

贡献

有关详细信息,请参阅贡献指南

安全性

如果您发现任何与安全相关的问题,请通过电子邮件freek@OFFLINE.be而不是使用问题跟踪器。

鸣谢

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件