Laravel-Anwendung absichern: eine praktische Checkliste

Andreas Kunisch, , 5 Min. Lesezeit

Laravel bringt viele Sicherheitsmechanismen schon mit, etwa CSRF-Schutz, Hashing und Query-Bindings. Die Lücken entstehen meist in Konfiguration und Anwendungscode, wo diese von den Defaults abweichen.

Diese Checkliste geht die Punkte durch, die in einem Sicherheits-Review einer Laravel-Anwendung regelmäßig auftauchen: von der Umgebungskonfiguration über Validierung und Autorisierung bis zu Logging und Updates. Sie richtet sich an Teams, die eine bestehende Anwendung härten oder ein neues Projekt von Anfang an richtig aufsetzen wollen.

Stimmt die Umgebungskonfiguration?

In Produktion muss APP_DEBUG auf false stehen und APP_ENV auf production. Ist APP_DEBUG aktiv, zeigt Laravel bei Fehlern vollständige Stacktraces inklusive Dateipfaden, Umgebungsvariablen und teilweise Datenbank-Queries. Für Angreifer ist das eine fertige Anleitung.

Die .env-Datei darf nie ins Repository gelangen und gehört in .gitignore. Secrets wie APP_KEY, Datenbank-Zugangsdaten und API-Keys werden ausschließlich über Umgebungsvariablen oder einen Secret-Manager gesetzt, nie hartcodiert in config()-Dateien. Außerhalb der config-Dateien ruft der Code nur config() auf. Wer dort env() verwendet, bekommt bei gecachter Konfiguration in Produktion falsche Werte.

Sind Abhängigkeiten aktuell und geprüft?

  • composer audit regelmäßig ausführen, am besten als Pflichtschritt in der CI-Pipeline und nicht nur ab und zu von Hand.
  • composer outdated nutzen, um veraltete Pakete zu identifizieren, und Updates in kleinen, testbaren Schritten einspielen.
  • Dependabot oder ein vergleichbares Tool für automatische Update-Pull-Requests aktivieren.
  • Laravel selbst, PHP und alle Pakete auf einer noch unterstützten Version halten. End-of-Life-Versionen erhalten keine Sicherheitspatches mehr.
  • Bei Paketen mit geringer Pflege prüfen, ob eine aktiv gewartete Alternative existiert.

Validieren Sie wirklich jede Eingabe?

Jeder Endpunkt, der Eingaben entgegennimmt, braucht eine eigene Form-Request-Klasse, keine Inline-Validierung im Controller. Das hält Validierungsregeln an einem Ort, macht sie testbar und verhindert, dass ein Feld vergessen wird.

Wichtig ist Vollständigkeit: Strings brauchen eine max-Länge, Zahlen min/max-Grenzen, Arrays eine max-Anzahl und validierte Einzelelemente, Enums die Rule::enum()-Regel. Validierungsregeln sollten den Datenbank-Constraints entsprechen. Fehlt bei einer varchar(255)-Spalte die passende max-Regel, sieht der Nutzer einen Datenbankfehler statt einer sauberen Validierungsmeldung.

Ist Mass Assignment abgesichert?

Jedes Eloquent-Model muss $fillable explizit definieren. $guarded = [] ist tabu. Ohne diese Einschränkung kann ein Angreifer über zusätzliche Formularfelder Spalten wie is_admin oder role setzen, die nie für die Massenzuweisung gedacht waren.

Felder, die ein Nutzer niemals selbst setzen darf, etwa Rollen oder interne Status, werden separat und explizit gesetzt. Ein create() oder update() mit dem vollen Request-Payload darf sie nicht berühren.

Prüfen Policies jede Autorisierung?

Für jedes Model, auf das Nutzer zugreifen, gehört eine Policy angelegt, und der Controller muss sie mit $this->authorize() auch wirklich aufrufen. Validierung prüft nur das Format der Daten. Ob der aktuelle Nutzer berechtigt ist, klärt erst die Policy.

Ein häufiger Fehler ist die fehlende Prüfung bei IDs in der URL: Ein Nutzer kann die ID in /invoices/42 einfach durchzählen. Ohne Policy-Check sieht er fremde Datensätze, auch wenn er eingeloggt ist.

Sind CSRF, Rate Limiting und Uploads abgesichert?

  • CSRF-Schutz ist für zustandsbehaftete Web-Routen standardmäßig aktiv. Eigene Webhook-Endpunkte gezielt und mit Begründung ausnehmen, keine ganzen Routengruppen pauschal.
  • Login, Registrierung und Passwort-Reset mit Rate Limiting versehen, zum Beispiel über throttle-Middleware, um Brute-Force- und Credential-Stuffing-Versuche zu begrenzen.
  • Hochgeladene Dateien über mimes und max in der Form Request einschränken, bei Bildern zusätzlich dimensions prüfen.
  • Dateinamen nie unverändert übernehmen. Sie werden mit Str::random(40) plus Original-Extension neu vergeben, und Uploads außerhalb des öffentlich erreichbaren Verzeichnisses oder über einen separaten Storage-Datenträger ablegen.

Sind HTTPS und Security-Header gesetzt?

HTTPS ist Pflicht, inklusive HSTS-Header, damit Browser auch bei einem versehentlichen HTTP-Aufruf konsequent auf die verschlüsselte Verbindung wechseln. Dazu kommen X-Frame-Options oder eine entsprechende Content-Security-Policy-Direktive, X-Content-Type-Options: nosniff und eine CSP, die externe Script-Quellen einschränkt.

Diese Header lassen sich zentral in einer Middleware oder am Webserver setzen, ohne sie pro Route einzeln zu konfigurieren. So bleibt die Konfiguration an einer Stelle wartbar, und neue Routen erben den Schutz automatisch.

Was gehört nicht ins Log?

Logs sind oft weniger geschützt als die Datenbank selbst und werden seltener auf Zugriffsrechte geprüft. Passwörter, Tokens, vollständige Kreditkartennummern und andere sensible Felder dürfen dort nicht landen, auch nicht in Exception-Kontexten oder Request-Dumps, die im Debug-Fall mitgeloggt werden.

Laravel erlaubt es, einzelne Felder über $hidden am Model oder gezielt vor dem Logging zu maskieren. Bei einer Modernisierung eines bestehenden Systems lohnt sich ein Blick in vorhandene Log-Dateien: Dort zeigt sich oft, ob ältere Fehlerbehandlung unbeabsichtigt sensible Daten mitschreibt.

Häufige Fragen

Wie oft sollte composer audit laufen?
Mindestens bei jedem Deployment, idealerweise als automatischer Schritt in der CI-Pipeline. Zusätzlich lohnt sich ein wöchentlicher Lauf unabhängig von Releases, da neue Schwachstellen in bereits installierten Paketen jederzeit bekannt werden können.
Reicht die Standard-Validierung von Laravel für Sicherheit aus?
Validierung allein schützt nicht vor allem. Sie verhindert fehlerhafte oder zu große Eingaben, ersetzt aber nicht die Autorisierungsprüfung über Policies und nicht die Einschränkung von Mass Assignment über $fillable. Alle drei Ebenen müssen zusammen greifen.
Muss jede Route Rate Limiting haben?
Nicht jede, aber alle, die Angreifer für automatisierte Versuche nutzen können: Login, Registrierung, Passwort-Reset und Formulare ohne Login-Zwang. Für interne, bereits authentifizierte Endpunkte ist das Risiko geringer, ein globales Grundlimit schadet aber auch dort nicht.
Was ist der häufigste Fehler bei Datei-Uploads?
Der Original-Dateiname wird unverändert gespeichert und teilweise sogar für die URL verwendet. Das erlaubt Path-Traversal-Versuche und macht erratbare Dateinamen möglich. Die Lösung ist ein zufällig generierter Name mit geprüfter Extension.
Lohnt sich ein Sicherheits-Review bei einer bestehenden, älteren Laravel-Anwendung?
Ja, besonders wenn das Projekt über Jahre gewachsen ist und mehrere Entwickler beteiligt waren. Typischerweise finden sich dort einzelne Routen ohne Policy-Check oder Modelle mit zu offenem $fillable, die bei der ursprünglichen Entwicklung unter Zeitdruck entstanden sind.

Weitere Ratgeber