CSP für Laravel konfigurieren

Content Security Policy in Laravel einrichten — mit spatie/laravel-csp, Blade-Nonces, Vite-Integration und schrittweisem Rollout von Report-Only zu Enforcement.

Laravel · Schritt für Schritt

Content Security Policy in Laravel

Content Security Policy (CSP) ist der wichtigste HTTP-Security-Header gegen Cross-Site Scripting (XSS). Er definiert, welche Ressourcen der Browser laden darf, und blockiert alles andere. CSP ist mit 35 von 166 Punkten der einflussreichste Header im Wolf-Agents Web Security Check.

Laravel bietet zwei Wege zur CSP-Implementierung: eine Custom Middleware für einfache Szenarien oder das Paket spatie/laravel-csp für automatische Nonce-Generierung und Policy-Klassen. Beide Ansätze werden in dieser Anleitung behandelt.

1 Schritt 1 von 4

Report-Only Modus starten

Beginnen Sie immer im Report-Only-Modus. Der Browser meldet CSP-Verstöße in der Konsole und an Ihren Reporting-Endpoint, blockiert aber keine Ressourcen. So können Sie die Policy in Ruhe anpassen, ohne dass Ihre Laravel-Anwendung für Besucher kaputt geht.

app/Http/Middleware/SecurityHeaders.php Report-Only
// app/Http/Middleware/SecurityHeaders.php
public function handle(Request $request, Closure $next): Response
{
    $response = $next($request);

    // Report-Only CSP — meldet Violations, blockiert nichts
    $csp = implode('; ', [
        "default-src 'self'",
        "script-src 'self'",
        "style-src 'self' 'unsafe-inline'",
        "img-src 'self' data: https:",
        "font-src 'self'",
        "connect-src 'self'",
        "object-src 'none'",
        "frame-ancestors 'self'",
        "base-uri 'self'",
        "form-action 'self'",
        "report-to csp-endpoint",
    ]);

    $response->headers->set(
        'Content-Security-Policy-Report-Only', $csp
    );

    $response->headers->set('Reporting-Endpoints',
        'csp-endpoint="https://ihre-domain.de/api/csp-report"'
    );

    return $response;
}
Umgebungsvariable nutzen

Steuern Sie den Modus über .env: CSP_REPORT_ONLY=true für Staging, CSP_REPORT_ONLY=false für Produktion. Nach Änderungen: php artisan config:cache.

2 Schritt 2 von 4

Violations analysieren und CSP anpassen

Öffnen Sie die Browser DevTools (F12) und prüfen Sie die Console. CSP-Violations erscheinen als Warnungen. Passen Sie die Policy an, bis keine unerwarteten Violations mehr auftreten.

Violation Lösung
Livewire-Script blockiert Nonce über @nonce oder spatie/laravel-csp nutzen
Vite HMR blockiert ws://localhost:5173 zu connect-src hinzufügen (nur Dev)
Google Fonts blockiert fonts.googleapis.com zu style-src, fonts.gstatic.com zu font-src
Blade Inline-Style blockiert 'unsafe-inline' in style-src belassen (üblich bei Laravel)
Externe API blockiert API-Domain zu connect-src hinzufügen
Lassen Sie die Policy mindestens 1 Woche im Report-Only-Modus laufen. Achten Sie besonders auf Livewire, Inertia.js und Alpine.js — diese Frameworks generieren dynamisches JavaScript.
3 Schritt 3 von 4

Enforcement aktivieren

Wenn keine unerwarteten Violations mehr auftreten, wechseln Sie von Report-Only zu Enforcement. Der Browser blockiert ab sofort nicht autorisierte Ressourcen.

app/Http/Middleware/SecurityHeaders.php Produktiv
// Enforcement — blockiert nicht autorisierte Ressourcen
$response->headers->set(
    'Content-Security-Policy', $csp
    // Statt 'Content-Security-Policy-Report-Only'
);
Testen Sie alle Seiten gründlich — inklusive Login, Dashboard, Admin-Bereich und E-Mail-Verifizierung. Behalten Sie den Reporting-Endpoints-Header bei, um auch im Enforcement-Modus Violations zu erfassen.
4 Schritt 4 von 4 · Fortgeschritten

Nonces mit spatie/laravel-csp und Vite

Für maximale Sicherheit verwenden Sie dynamische Nonces. Das Paket spatie/laravel-csp generiert pro Request einen Nonce und integriert sich nahtlos mit Blade und Vite. So können Sie 'unsafe-inline' aus der script-src entfernen.

Terminal Installation
# spatie/laravel-csp installieren
composer require spatie/laravel-csp
php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider" --tag="config"
config/csp.php Konfiguration
// config/csp.php
return [
    'enabled' => true,
    'report_only' => env('CSP_REPORT_ONLY', true),
    'report_uri' => env('CSP_REPORT_URI', ''),
    'policy' => App\Policies\CspPolicy::class,
];
app/Policies/CspPolicy.php Policy-Klasse
// app/Policies/CspPolicy.php
namespace App\Policies;

use Spatie\Csp\Directive;
use Spatie\Csp\Keyword;
use Spatie\Csp\Policies\Policy;

class CspPolicy extends Policy
{
    public function configure()
    {
        $this
            ->addDirective(Directive::DEFAULT, Keyword::SELF)
            ->addDirective(Directive::SCRIPT, Keyword::SELF)
            ->addNonceForDirective(Directive::SCRIPT)
            ->addDirective(Directive::STYLE, Keyword::SELF)
            ->addDirective(Directive::STYLE, Keyword::UNSAFE_INLINE)
            ->addDirective(Directive::IMG, Keyword::SELF)
            ->addDirective(Directive::IMG, 'data:')
            ->addDirective(Directive::IMG, 'https:')
            ->addDirective(Directive::FONT, Keyword::SELF)
            ->addDirective(Directive::CONNECT, Keyword::SELF)
            ->addDirective(Directive::OBJECT, Keyword::NONE)
            ->addDirective(Directive::BASE, Keyword::SELF)
            ->addDirective(Directive::FORM_ACTION, Keyword::SELF)
            ->addDirective(Directive::FRAME_ANCESTORS, Keyword::SELF);
    }
}
Blade-Template Nonces
<!-- Blade-Template: Nonce von spatie/laravel-csp -->
<script nonce="{{ csp_nonce() }}">
  console.log('Dieses Script ist autorisiert');
</script>

<!-- Vite-Assets erhalten Nonces automatisch -->
@vite(['resources/js/app.js'])
app/Providers/AppServiceProvider.php Vite
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Vite;

public function boot(): void
{
    // Nonce für alle Vite-Assets generieren
    Vite::useCspNonce();
}
Vite + Nonces seit Laravel 10

Mit Vite::useCspNonce() generiert Laravel automatisch einen Nonce für alle @vite-Tags. Der Nonce wird in den CSP-Header und in die HTML-Attribute eingefügt — keine manuelle Konfiguration nötig.

Häufige Fehler bei CSP in Laravel

Livewire-Kompatibilität

Livewire 3 injiziert dynamisches JavaScript. Ohne Nonces oder 'unsafe-inline' in script-src werden Livewire-Komponenten nicht funktionieren. Lösung: Nonces via spatie/laravel-csp nutzen.

Vite HMR im Development

Vites Hot Module Replacement braucht WebSocket-Zugriff. Fügen Sie ws://localhost:5173 zu connect-src hinzu — aber nur in der Development-Umgebung, nicht in Produktion.

Config-Cache vergessen

Nach Änderungen an .env (z.B. CSP_REPORT_ONLY) muss php artisan config:cache ausgeführt werden. Ohne Cache-Clear werden die alten Werte verwendet.

Doppelte CSP-Header

Wenn sowohl Custom Middleware als auch spatie/laravel-csp aktiv sind, entstehen zwei CSP-Header. Verwenden Sie nur einen Ansatz — nicht beide gleichzeitig.

Compliance-Anforderungen

Content Security Policy ist eine zentrale Anforderung mehrerer Compliance-Frameworks und besonders relevant für Laravel-Anwendungen, die sensible Daten verarbeiten.

PCI DSS 4.0 — Requirement 6.4.3 fordert explizit CSP-Header für alle Seiten, die Zahlungsdaten verarbeiten. Laravel-Anwendungen mit Stripe oder PayPal benötigen CSP.
OWASP ASVS — Level 2 (V14.4.3) fordert CSP mit Nonces oder Hashes. spatie/laravel-csp erfüllt diese Anforderung direkt.
NIS2 — Artikel 21 fordert technische Maßnahmen zum Schutz vor bekannten Angriffsvektoren. CSP schützt gegen XSS — den häufigsten Angriffstyp auf Webanwendungen.

Wie steht Ihre Domain bei Content Security Policy?

Prüfen Sie es jetzt — kostenlos, ohne Registrierung, mit 166 Prüfpunkte.