security: Andere Geräte abmelden, IP-weites Login-Limit, dauerhafte Sperrmarke, Passwortregeln, CSP im Melde-Modus
Restliche Phase 4 aus dem Sicherheitsaudit 07.10.2026: - Sicherheitsseite: „Andere Geräte abmelden“ rotiert remember_token und beendet alle anderen Sessions, die aktuelle bleibt (nicht während einer Impersonation). - LoginIpThrottle: 20 Fehlversuche je IP in 15 Minuten über alle Konten. Gezählt über das Failed-Event, damit Livewire-Login und Fortifys POST /login denselben Zähler teilen; Erfolg setzt ihn nicht zurück. - Sperrmarke für Sessions in users.sessions_revoked_at_ms statt nur im Cache — übersteht cache:clear. Die alte Cache-Marke zählt übergangsweise mit und kann im nächsten Release entfallen. - Neue und geänderte Passwörter: mindestens 12 Zeichen, in Produktion zusätzlich uncompromised() (HIBP, k-Anonymität). Bestehende bleiben gültig. - Content-Security-Policy-Report-Only in Produktion; Meldungen über /csp-report nach storage/logs/csp-*.log, je Meldung höchstens einmal pro Stunde, ohne Query und tokenartige Pfadsegmente. Migration: users.sessions_revoked_at_ms (users hat FULLTEXT, 25 MB, Sekunden). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9c4962fbe0
commit
5dfd622141
27 changed files with 601 additions and 39 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\Auth\SessionRevocation;
|
||||
use Closure;
|
||||
use Illuminate\Auth\SessionGuard;
|
||||
|
|
@ -11,8 +12,8 @@ use Symfony\Component\HttpFoundation\Response;
|
|||
|
||||
/**
|
||||
* Meldet eine Session ab, die vor der letzten Sperre ihres Kontos entstanden
|
||||
* ist (SessionRevocation, Audit K-04). Kostet je angemeldetem Request einen
|
||||
* Cache-Zugriff; Gäste lösen keinen aus.
|
||||
* ist (SessionRevocation, Audit K-04). Die Marke steht am geladenen User;
|
||||
* bis zum Release nach v1.6.0 kommt ein Cache-Zugriff dazu (Übergang).
|
||||
*/
|
||||
class EnsureSessionIsNotRevoked
|
||||
{
|
||||
|
|
@ -33,7 +34,7 @@ class EnsureSessionIsNotRevoked
|
|||
|
||||
$user = $guard->user();
|
||||
|
||||
if ($user === null || ! $this->revocation->isRevoked((int) $user->getAuthIdentifier(), $request->session())) {
|
||||
if (! $user instanceof User || ! $this->revocation->isRevoked($user, $request->session())) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Support\ContentSecurityPolicy;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
|
@ -11,8 +12,7 @@ use Symfony\Component\HttpFoundation\Response;
|
|||
* Reverse-Proxy gesetzt, damit der Schutz auch bei Proxy-Wechsel bleibt.
|
||||
* Bereits vorhandene Header (z. B. vom Proxy) werden nicht überschrieben.
|
||||
* HSTS nur auf HTTPS-Antworten und laut config/security.php.
|
||||
* CSP fehlt absichtlich: Livewire/Alpine/Vite brauchen eine eigene
|
||||
* Inline-Script-Strategie — separat einführen, nicht nebenbei.
|
||||
* CSP vorerst nur im Melde-Modus (ContentSecurityPolicy, config/security.php).
|
||||
*/
|
||||
class SecurityHeaders
|
||||
{
|
||||
|
|
@ -31,6 +31,12 @@ class SecurityHeaders
|
|||
}
|
||||
}
|
||||
|
||||
if (config('security.csp.report_only')
|
||||
&& str_contains((string) $response->headers->get('Content-Type'), 'text/html')
|
||||
&& ! $response->headers->has('Content-Security-Policy-Report-Only')) {
|
||||
$response->headers->set('Content-Security-Policy-Report-Only', ContentSecurityPolicy::header());
|
||||
}
|
||||
|
||||
if ($request->isSecure() && config('security.hsts.enabled') && ! $response->headers->has('Strict-Transport-Security')) {
|
||||
$response->headers->set('Strict-Transport-Security', 'max-age='.(int) config('security.hsts.max_age'));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue