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.
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.
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
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;
} 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.
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 |
Enforcement aktivieren
Wenn keine unerwarteten Violations mehr auftreten, wechseln Sie von Report-Only zu Enforcement. Der Browser blockiert ab sofort nicht autorisierte Ressourcen.
// Enforcement — blockiert nicht autorisierte Ressourcen
$response->headers->set(
'Content-Security-Policy', $csp
// Statt 'Content-Security-Policy-Report-Only'
); Reporting-Endpoints-Header bei, um auch im Enforcement-Modus Violations zu erfassen. 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.
# spatie/laravel-csp installieren
composer require spatie/laravel-csp
php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider" --tag="config" // 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
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: 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
use Illuminate\Support\Facades\Vite;
public function boot(): void
{
// Nonce für alle Vite-Assets generieren
Vite::useCspNonce();
} 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.
Wie steht Ihre Domain bei Content Security Policy?
Prüfen Sie es jetzt — kostenlos, ohne Registrierung, mit 166 Prüfpunkte.