From 058612dbe600c47d91bf41feca9fff86b859758d Mon Sep 17 00:00:00 2001 From: vk Date: Wed, 11 Feb 2026 23:19:31 +0100 Subject: [PATCH] =?UTF-8?q?[P1=20Security]=20Session=20Security=20Hardenin?= =?UTF-8?q?g=20=E2=80=93=20Encryption=20&=20Secure=20Cookies=20(vibe-kanba?= =?UTF-8?q?n=2048c2078b)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Security Audit: Session-Konfiguration härten ### Problem Die aktuelle `.env.example` hat unsichere Session-Defaults: ```env SESSION_ENCRYPT=false # Session-Daten unverschlüsselt in der DB SESSION_SECURE_COOKIE= # Nicht gesetzt – Cookie wird auch über HTTP gesendet SESSION_DOMAIN=null # Nicht eingeschränkt ``` Da die App eine Custom Nostr-basierte Auth verwendet (`app/Auth/NostrSessionGuard.php`) und der `pubkey` in der Session gespeichert wird, ist die Session ein attraktives Angriffsziel. Unverschlüsselte Sessions in der Datenbank bedeuten, dass ein DB-Leak sofort alle aktiven Sessions kompromittiert. ### Lösung **1. `.env.example` aktualisieren:** ```env SESSION_ENCRYPT=true SESSION_SECURE_COOKIE=true ``` **2. Session-Config in `config/session.php` prüfen:** Falls `config/session.php` nicht existiert (Laravel 12 Slim-Struktur), muss die Config ggf. mit `php artisan config:publish session` veröffentlicht werden. Prüfe ob die folgenden Werte korrekt gesetzt sind: ```php 'encrypt' => env('SESSION_ENCRYPT', true), // Default auf true ändern 'secure' => env('SESSION_SECURE_COOKIE', true), // Default auf true ändern 'http_only' => true, // Bereits Standard 'same_site' => 'lax', // Bereits Standard ``` **3. Cookie-Sicherheit:** - `http_only` verhindert JavaScript-Zugriff auf Session-Cookies (Schutz gegen XSS-Cookie-Theft) - `secure` erzwingt HTTPS-only Übertragung - `same_site: lax` schützt gegen CSRF bei Top-Level-Navigation ### Betroffene Dateien - `.env.example` – Default-Werte aktualisieren - `config/session.php` – Falls vorhanden, Defaults härten. Falls nicht vorhanden, mit `php artisan config:publish session` erstellen und anpassen ### Vorgehen 1. Prüfe ob `config/session.php` existiert (bei Laravel 12 ist es möglicherweise nicht veröffentlicht) 2. Falls nicht vorhanden: `php artisan config:publish session --no-interaction` 3. Sichere Defaults setzen (encrypt=true, secure=true) 4. `.env.example` aktualisieren 5. Einen Pest-Test schreiben, der verifiziert dass Session-Cookies die korrekten Flags haben (secure, httpOnly, sameSite) 6. `vendor/bin/pint --dirty` und `php artisan test --compact` ### Hinweis - In der lokalen Entwicklung (`APP_ENV=local`) kann `SESSION_SECURE_COOKIE=false` gesetzt werden, damit HTTP funktioniert - Der Default in der Config sollte aber `true` sein, damit Produktion abgesichert ist - Die `.env` Datei selbst wird NICHT bearbeitet (nur `.env.example`) ### Akzeptanzkriterien - `SESSION_ENCRYPT=true` als Default in `.env.example` - `SESSION_SECURE_COOKIE=true` als Default in `.env.example` - Session-Config verwendet sichere Defaults - Tests verifizieren Cookie-Sicherheitsflags - Lokale Entwicklung bleibt funktional (über `.env` Override) --- .env.example | 3 ++- config/session.php | 4 +-- tests/Feature/SessionSecurityTest.php | 39 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 tests/Feature/SessionSecurityTest.php diff --git a/.env.example b/.env.example index 904d8e6..aecddb1 100644 --- a/.env.example +++ b/.env.example @@ -34,7 +34,8 @@ DB_PASSWORD_EINUNDZANZIG=secret SESSION_DRIVER=database SESSION_LIFETIME=120 -SESSION_ENCRYPT=false +SESSION_ENCRYPT=true +SESSION_SECURE_COOKIE=true SESSION_PATH=/ SESSION_DOMAIN=null diff --git a/config/session.php b/config/session.php index f0b6541..608b6d6 100644 --- a/config/session.php +++ b/config/session.php @@ -47,7 +47,7 @@ return [ | */ - 'encrypt' => env('SESSION_ENCRYPT', false), + 'encrypt' => env('SESSION_ENCRYPT', true), /* |-------------------------------------------------------------------------- @@ -169,7 +169,7 @@ return [ | */ - 'secure' => env('SESSION_SECURE_COOKIE'), + 'secure' => env('SESSION_SECURE_COOKIE', true), /* |-------------------------------------------------------------------------- diff --git a/tests/Feature/SessionSecurityTest.php b/tests/Feature/SessionSecurityTest.php new file mode 100644 index 0000000..1d70146 --- /dev/null +++ b/tests/Feature/SessionSecurityTest.php @@ -0,0 +1,39 @@ +toBeTrue('http_only should default to true'); + expect($config['same_site'])->toBe('lax', 'same_site should default to lax'); +}); + +it('defaults session encryption to true in config', function () { + $configContent = file_get_contents(base_path('config/session.php')); + + expect($configContent)->toContain("env('SESSION_ENCRYPT', true)"); +}); + +it('defaults secure cookie to true in config', function () { + $configContent = file_get_contents(base_path('config/session.php')); + + expect($configContent)->toContain("env('SESSION_SECURE_COOKIE', true)"); +}); + +it('has secure session defaults in env example', function () { + $envExample = file_get_contents(base_path('.env.example')); + + expect($envExample)->toContain('SESSION_ENCRYPT=true'); + expect($envExample)->toContain('SESSION_SECURE_COOKIE=true'); +}); + +it('sets httponly and samesite flags on session cookie', function () { + $response = $this->get('/'); + + $sessionCookie = collect($response->headers->getCookies()) + ->first(fn ($cookie) => $cookie->getName() === config('session.cookie')); + + expect($sessionCookie)->not->toBeNull(); + expect($sessionCookie->isHttpOnly())->toBeTrue('Session cookie should be HttpOnly'); + expect($sessionCookie->getSameSite())->toBe('lax', 'Session cookie should have SameSite=lax'); +});