spatie/laravel-csp

repository·main·Indexed 21 days ago

https://github.com/spatie/laravel-csp

A Laravel package for easily setting Content Security Policy (CSP) headers to protect applications from malicious scripts and data exfiltration. It features built-in presets for common third-party services (e.g., Google, Stripe, Sentry), support for nonces via Blade directives, and integration with Laravel Vite. Policies can be applied globally or per-route via the AddCspHeaders middleware, or rendered as HTML <meta> tags using the @cspMetaTag directive.

Tokens
7.2K
Snippets
30
Records
33
Agent score
74%

What's inside spatie/laravel-csp

  1. Output CSP via Meta Tags

    main

    If you cannot control HTTP headers (e.g., on a static page) or if your CSP header is too large, you can output the policy using HTML <meta> tags. Use the following Blade directives in your <head>:

    • @cspMetaTag: Renders all configured presets.
    • @cspMetaTag(Preset::class): Renders a specific preset.
    • @cspMetaTagReportOnly(Preset::class): Renders a specific preset in report-only mode.
    <head>
        @cspMetaTag
    </head>
  2. Integrate with Laravel Vite Nonce

    main

    If you are using Laravel Vite, you can integrate its nonce handling with this package by implementing the Spatie\Csp\Nonce\NonceGenerator interface. This ensures that the nonce used in your CSP headers matches the one Vite uses for its assets.

    1. Create a LaravelViteNonceGenerator class.
    2. Update config/csp.php to use your new generator class in the nonce_generator key.
    namespace App\Support;
    
    use Illuminate\Support\Facades\Vite;
    use Spatie\Csp\Nonce\NonceGenerator;
    
    class LaravelViteNonceGenerator implements NonceGenerator
    {
        public function generate(): string
        {
            return Vite::useCspNonce();
        }
    }
    // config/csp.php
    'nonce_generator' => App\Support\LaravelViteNonceGenerator::class,
  3. Use built-in CSP presets

    main
    The package includes a variety of pre-configured presets for common third-party services (e.g., Google, Stripe, Sentry, Algolia). To use them, register the desired preset names in your config/csp.php file under either the presets key (for active enforcement) or the report_only_presets key (for report-only mode).
  4. Migrate from 2.x to 3.x: Replace Policies with Presets

    main

    In version 3.x, individual policies have been replaced by presets. Unlike the previous version, v3 allows you to use multiple presets simultaneously.

    To upgrade your configuration, you must update the configuration keys:

    • Replace policy with presets (an array of preset classes).
    • Replace report_only_policy with report_only_presets (an array of preset classes).

    If your policy only adds static directives without complex logic, you can skip creating a preset class entirely and instead register them directly in the configuration file using the directives and report_only_directives keys.

    // Before (2.x)
    return [
        'policy' => Spatie\Csp\Policies\Basic::class,
        'report_only_policy' => '',
    ];
    
    // After (3.x)
    return [
        'presets' => [
            Spatie\Csp\Presets\Basic::class,
        ],
        'report_only_presets' => [
            // 
        ],
    ];
  5. Create a CSP Preset

    main

    A Preset is a class used to group CSP directives. You can define a custom preset by implementing the Spatie\Csp\Preset interface and its configure(Policy $policy) method. This allows you to define reusable sets of rules, such as a Basic preset that only allows resources from your own domain.

    When adding directives via the add() method:

    • You do not need to manually wrap keywords like 'self', 'none', or 'unsafe-inline' in quotes; the package handles this automatically.
    • Hashes (e.g., script/style hashes) are also automatically quoted.
    • You can pass an array of directives to apply the same value to multiple directives.
    • You can pass an array of values to apply multiple keywords/domains to a single directive.
    • For directives that require no value (like upgrade-insecure-requests), use Value::NO_VALUE.
    namespace App\Support;
    
    use Spatie\Csp\Directive;
    use Spatie\Csp\Keyword;
    use Spatie\Csp\Policy;
    use Spatie\Csp\Preset;
    
    class MyCspPreset implements Preset
    {
        public function configure(Policy $policy): void
        {
            $policy->add(Directive::SCRIPT, 'www.google.com');
        }
    }
  6. Use Nonces for Inline Scripts and Styles

    main

    To allow inline <script> and <style> tags without using 'unsafe-inline', use a nonce (a unique number per request).

    1. Configure the Policy: Add the nonce to the relevant directives using addNonce().
    2. Apply to HTML: Use the @cspNonce Blade directive on your tags.
    3. Dynamic Scripts: For scripts generated dynamically (e.g., by third-party libraries), retrieve the nonce using app('csp-nonce').
    // 1. Policy configuration
    public function configure(Policy $policy): void
    {
        $policy
             ->add(Directive::SCRIPT, Keyword::SELF)
             ->add(Directive::STYLE, Keyword::SELF)
             ->addNonce(Directive::SCRIPT)
             ->addNonce(Directive::STYLE);
    }
    {{-- 2. Blade usage --}}
    <style @cspNonce>
       ...
    </style>
    
    <script @cspNonce>
       ...
    </script>
    
    {{-- 3. Dynamic retrieval --}}
    @googlefonts(['nonce' => app('csp-nonce')])
  7. Apply CSP headers via Middleware

    main

    To apply CSP headers to your application, you must register the Spatie\Csp\AddCspHeaders middleware. You can apply it globally, to specific routes, or to route groups.

    // 1. Register as global middleware in bootstrap/app.php
    use Spatie\Csp\AddCspHeaders;
    
    ->withMiddleware(function (Middleware $middleware) {
         $middleware->append(AddCspHeaders::class);
    })
    
    // 2. Apply to a specific route
    Route::get('my-page', 'MyController')
        ->middleware(AddCspHeaders::class);
    
    // 3. Apply a specific preset to a route (overrides config/csp.php)
    Route::get('my-page', 'MyController')
        ->middleware(AddCspHeaders::class . ':' . MyPreset::class);
  8. Refactor custom Policies to Presets in 3.x

    main

    When upgrading custom policy classes to the new Preset system in version 3.x, you must apply the following changes:

    1. Interface Change: Implement Spatie\Csp\Preset instead of extending Spatie\Csp\Policies\Policy.
    2. Method Signature: Update the configure method to accept a Spatie\Csp\Policy instance as an argument: public function configure(Policy $policy): void.
    3. Method Renaming:
      • Rename addDirective() to add().
      • Rename addNonceForDirective() to addNonce().

    Note: The methods reportOnly and shouldBeApplied are no longer supported in presets.

    use Spatie\Csp\Directive;
    use Spatie\Csp\Keyword;
    use Spatie\Csp\Policy;
    use Spatie\Csp\Preset;
    
    class MyPreset implements Preset
    {
        public function configure(Policy $policy): void
        {
            $policy
                ->add(Directive::SCRIPT, Keyword::SELF)
                ->addNonce(Directive::SCRIPT);
        }
    }
  9. Register Presets in Configuration

    main

    After creating a custom preset, you must register it in your config/csp.php file under the presets key. The order matters: presets are applied in the order they are listed in the array.

    'presets' => [
        Spatie\Csp\Presets\Basic::class,
        App\Support\MyCspPreset::class,
    ],
  10. Configure the CSP settings in config/csp.php

    main

    The config/csp.php file allows you to define presets, global directives, reporting endpoints, and nonce settings.

    Key configuration options include:

    • presets: An array of classes implementing Spatie\Csp\Preset that determine which headers are set.
    • directives: Global CSP directives to add to the policy.
    • report_only_presets: Presets used for a report-only policy (useful for testing without breaking functionality).
    • report_uri: The URL where violations are reported (e.g., via https://report-uri.com/).
    • enabled: Boolean to enable or disable CSP headers globally.
    • nonce_enabled: Boolean to enable/disable automatic nonce generation.
    return [
        'presets' => [
            Spatie\Csp\Presets\Basic::class,
        ],
        'directives' => [
            // [Directive::SCRIPT, [Keyword::UNSAFE_EVAL, Keyword::UNSAFE_INLINE]],
        ],
        'report_only_presets' => [],
        'report_only_directives' => [],
        'report_uri' => env('CSP_REPORT_URI', ''),
        'report_only_uri' => env('CSP_REPORT_ONLY_URI', ''),
        'report_to' => env('CSP_REPORT_TO', ''),
        'report_only_to' => env('CSP_REPORT_ONLY_TO', ''),
        'reporting_endpoints' => [],
        'enabled' => env('CSP_ENABLED', true),
        'enabled_while_hot_reloading' => env('CSP_ENABLED_WHILE_HOT_RELOADING', false),
        'nonce_generator' => Spatie\Csp\Nonce\RandomString::class,
        'nonce_enabled' => env('CSP_NONCE_ENABLED', true),
    ];