Project-Image-Uploader/FeatureRequests/FEATURE_REQUEST-security.md
matthias.lotz 6332b82c6a Feature Request: admin session security
- replace bearer auth with session+CSRF flow and add admin user directory

- update frontend moderation flow, force password change gate, and new CLI

- refresh changelog/docs/feature plan + ensure swagger dev experience
2025-11-23 21:18:42 +01:00

20 KiB
Raw Blame History

  1. erstelle ein Branch namens feature/security aus dem aktuellen main Branch.
  2. erstelle eine Datei FeatureRequests/FEATURE_PLAN-security.md in der du die Umsetzungsaufgaben dokumentierst (siehe unten) und darin die TODO Liste erstellst und aktuallisierst.
  3. Stelle mir Fragen bezüglich der Umsetzung
  4. Verstehe, wie bisher im Frontend die UI aufgebaut ist (modular, keine inline css, globale app.css)
  5. Implementiere die untenstehenden Aufgaben Schritt für Schritt.

FEATURE_REQUEST: Security — Server-seitige Sessions für Admin-API

Umsetzungsaufgaben (konkret & eindeutig für KI / Entwickler)

Die folgenden Aufgaben sind Schritt-für-Schritt auszuführen. Jede Aufgabe enthält das gewünschte Ergebnis und minimalen Beispielcode oder Befehle. Die KI/Entwickler sollen die Änderungen als Code-Patches anlegen, Tests hinzufügen und die Dokumentation aktualisieren.

  1. Session-Store & Session-Konfiguration

    • Ziel: Server-seitige Sessions für Admin-Login verfügbar machen.
    • Schritte:
      • Installiere Packages: npm install express-session connect-sqlite3 --save (Backend).
      • In backend/src/server.js (oder Entrypoint) konfiguriere express-session mit connect-sqlite3:
        const session = require('express-session');
        const SQLiteStore = require('connect-sqlite3')(session);
        app.use(session({
          store: new SQLiteStore({ db: 'sessions.sqlite' }),
          secret: process.env.ADMIN_SESSION_SECRET,
          resave: false,
          saveUninitialized: false,
          cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'Strict', maxAge: 8*60*60*1000 }
        }));
        
    • Abnahme: Session-Cookie (sid) wird gesetzt nach Login, cookie-Flags korrekt.
  2. Login-Endpoint (Admin)

    • Ziel: Admin kann sich mit Benutzername/Passwort anmelden; Backend erstellt Session.
    • Schritte:
      • Füge POST /auth/login hinzu, prüft Credentials (z. B. gegen environment-stored admin user/pass oder htpasswd), legt req.session.user = { role: 'admin' } an und req.session.csrfToken = randomHex() an.
      • Rückgabe: 200 OK. Cookie wird automatisch gesetzt (credentials: 'include' vom Frontend).
    • Abnahme: Nach POST /auth/login existiert req.session.user und req.session.csrfToken.
  3. CSRF-Endpoint + Middleware

    • Ziel: Session-gebundenen CSRF-Token ausgeben und Requests schützen.
    • Schritte:
      • Endpoint GET /auth/csrf-token gibt { csrfToken: req.session.csrfToken } zurück (nur wenn eingeloggt).
      • Middleware requireCsrf prüft req.headers['x-csrf-token'] === req.session.csrfToken für state-changing Methoden.
    • Abnahme: state-changing Admin-Requests ohne oder mit falschem X-CSRF-Token bekommen 403.
  4. Backend-Auth-Middleware für Admin-API

    • Ziel: Alle /api/admin/* Endpoints prüfen Session statt Client-Token.
    • Schritte:
      • Ersetze oder erweitere bestehende Admin-Auth-Middleware (middlewares/auth.js) so, dass sie req.session.user && req.session.user.role === 'admin' prüft; falls nicht gesetzt → 403.
    • Abnahme: GET /api/admin/* ohne Session → 403; mit gültiger Session → durchgelassen.
  5. Frontend-Änderungen (adminApi)

    • Ziel: Frontend sendet keine Admin-Bearer-Tokens mehr; verwendet Cookie-Session + CSRF-Header.
    • Schritte:
      • Entferne in frontend/src/services/adminApi.js die Abhängigkeit von process.env.REACT_APP_ADMIN_API_KEY.
      • Passe adminFetch/adminRequest an: bei Requests setze credentials: 'include' und füge X-CSRF-Token Header (Token bezieht Frontend über GET /auth/csrf-token nach Login).
      • Dokumentiere in frontend/README oder Code-Kommentar, dass Admin-UI nach Login fetch('/auth/csrf-token', { credentials: 'include' }) aufruft.
    • Abnahme: adminApi.js sendet keine Bearer-Header; admin Requests beinhalten credentials: 'include' und X-CSRF-Token.
  6. Entfernen von Admin-Key aus Frontend Build/Compose/Dockerfile

    • Ziel: Keine Weitergabe von ADMIN_API_KEY an frontend und kein Kopieren sensibler .env in Frontend-Image.
    • Schritte:
      • Entferne Zeile - REACT_APP_ADMIN_API_KEY=${ADMIN_API_KEY} aus docker/prod/docker-compose.yml.
      • Entferne COPY docker/prod/frontend/config/.env ./.env aus docker/prod/frontend/Dockerfile oder stelle sicher, dass diese Datei ausschließlich non-sensitive Keys enthält.
      • Dokumentiere in FeatureRequests/FEATURE_REQUEST-security.md welche Keys im runtime-env.sh erlaubt sind (z. B. API_URL, APP_VERSION).
    • Abnahme: docker-compose enthält keine Übergabe an frontend; Build und Image enthalten keine Production-Secrets.
  7. Secrets-Handling / Deployment

    • Ziel: Secrets nur in Backend-Umgebung bereitstellen.
    • Schritte:
      • Setze ADMIN_API_KEY und ADMIN_SESSION_SECRET in CI/CD Secrets oder Docker Secrets und referenziere sie nur im backend Service.
      • Beispiel-Dokumentation für CI: wie man Secret in GitLab/GitHub Actions setzt und an Container übergibt.
    • Abnahme: Secrets sind nicht in Repo/Images; docker inspect der frontend-Container zeigt keinen Admin-Key.
  8. Tests & CI-Checks

    • Ziel: Automatisierte Verifikation der Sicherheitsregeln.
    • Schritte:
      • Integrationstest 1: GET /api/admin/some ohne Session → expect 403.
      • Integrationstest 2: POST /auth/login with admin credentials → expect Set-Cookie; then GET /auth/csrf-token → receive token; then POST /api/admin/action with X-CSRF-Token → expect 200.
      • Build-scan-Check: CI Schritt rg REACT_APP_ADMIN_API_KEY build/ || true fails if found.
    • Abnahme: Tests grün; CI verweigert Merge wenn Build enthält Admin-Key.
  9. Key-Leak Reaktion (konkrete Anweisungen)

    • Ziel: Falls ein Admin-Key geleakt wurde, sichere, koordinierte Rotation.
    • Schritte:
      • Scannen: trufflehog --regex --entropy=True . oder git-secrets scan.
      • Entfernen: git-filter-repo --replace-text passwords.txt oder bfg --replace-text passwords.txt (siehe docs).
      • Rotation: Erzeuge neuen Key (openssl rand -hex 32), update CI secret, redeploy Backend.
    • Hinweis: History-Rewrite ist invasiv; kommuniziere mit Team und informiere Contributors.
  10. Dokumentation

  • Ziel: Abschlussdokumentation aktualisiert.
  • Schritte:
    • Ergänze AUTHENTICATION.md um Login/Session/CSRF-Flow und Secret-Handling.
    • Ergänze FeatureRequests/FEATURE_REQUEST-security.md mit Implementations-Links (Patches/PRs).
  1. MIGRATION-GUIDE Anpassung (unbedingt)
  • Ziel: Die frontend/MIGRATION-GUIDE.md spiegelt nicht mehr den sicheren Produktions-Workflow. Sie muss aktualisiert werden, damit Entwickler/KI keine unsicheren Anweisungen (Admin-Key im Frontend) ausführen.
  • Aktueller Stand (zu prüfen): Die MIGRATION-GUIDE enthält Anweisungen, REACT_APP_ADMIN_API_KEY in frontend/.env zu setzen und dieselbe Variable an frontend im docker-compose.yml weiterzugeben. Dies steht im direkten Widerspruch zur hier geforderten serverseitigen Session-Lösung.
  • Erforderliche Änderungen in frontend/MIGRATION-GUIDE.md (konkret):
    • Entferne oder ersetze alle Anweisungen, die REACT_APP_ADMIN_API_KEY in Frontend .env oder Build-Umgebungen für Production setzen.
    • Ersetze Fetch-/Axios-Beispiele, die Authorization: Bearer ${process.env.REACT_APP_ADMIN_API_KEY} setzen, durch die neue Anleitung: Login → GET /auth/csrf-tokenfetch(..., { credentials: 'include', headers: { 'X-CSRF-Token': csrfToken } }).
    • Passe das Docker-Beispiel an: ADMIN_API_KEY darf nur dem backend-Service übergeben werden; entferne die Weitergabe an frontend (Zeile - REACT_APP_ADMIN_API_KEY=${ADMIN_API_KEY}).
    • Ersetze lokale Testanweisungen, die Frontend mit REACT_APP_ADMIN_API_KEY starten, durch Login-/Session-Testschritte (siehe Tasks 2/3/8).
    • Ergänze Hinweis zur CI/Build-Scan-Prüfung: CI muss prüfen, dass gebaute build/ keine Admin-Key-Strings enthält.
  • Abnahme: frontend/MIGRATION-GUIDE.md enthält keine Production-Anweisungen, die Admin-Secrets ins Frontend bringen; stattdessen ist der Session-Flow dokumentiert und verlinkt.

Hinweis für die Implementierung

  • Ergänze in FeatureRequests/FEATURE_REQUEST-security.md einen Link/Verweis zur überarbeiteten MIGRATION-GUIDE-Version in der PR/Release-Notes, damit Reviewer die Änderung nachvollziehen können.

Rolle der implementierenden KI/Dev

  • Erzeuge konkrete Code-Patches, führe lokale Tests aus, öffne PR mit Änderungen und Tests.
  • Stelle sicher, dass alle Abnahme-Kriterien (oben) automatisiert oder manuell prüfbar sind.

Mit diesen Aufgaben sind die vorher offenen Fragen in eindeutige, ausführbare Schritte übersetzt. Bitte bestätige, welche Aufgaben ich automatisch umsetzen soll (z. B. 1 = Compose/Docker-Änderungen; 2 = Frontend adminApi.js Patch; 3 = Backend Session+CSRF minimal-Implementierung; oder all).

Hintergrund (Ist-Stand)

  • Aktuell existieren folgende sicherheitskritische Zustände im Repository:
    • frontend/.env enthält REACT_APP_ADMIN_API_KEY in der Arbeitskopie (lokal). Die Datei ist in .gitignore und wird nicht ins Git-Repository getrackt, ist aber sensibel und darf nicht in Builds/Images gelangen.
    • docker/prod/docker-compose.yml injiziert REACT_APP_ADMIN_API_KEY=${ADMIN_API_KEY} in den frontend-Service — der Key kann so in den gebauten Frontend-Bundles landen.
    • frontend/src/services/adminApi.js liest process.env.REACT_APP_ADMIN_API_KEY und sendet den Bearer-Token clientseitig mit Admin-Requests.
    • Das Production-Frontend-Dockerfile kopiert docker/prod/frontend/config/.env in das Laufzeit-Image und führt zur Startzeit ein env.sh aus, das env-config.js erzeugt (window._env_), was sensible Werte im Browser verfügbar machen kann, falls sie in .env landen.
    • Die Moderation-Weboberfläche ist zusätzlich durch htpasswd/nginx HTTP Basic Auth geschützt — das schützt das UI, aber nicht die API-Endpoints ausreichend.

Problemstellung (warum es ein Problem ist)

  • Ein im Frontend sichtbarer Admin-Key ist öffentlich und ermöglicht Missbrauch (API-Calls mit Admin-Rechten von jedem Browser).
  • Das serverseitige Secret ADMIN_API_KEY wird derzeit in Artefakte/Images injiziert und kann geleakt werden.
  • HTTP Basic Auth vor der UI ist nützlich, aber kein Ersatz für serverseitige API-Authentifizierung; API-Endpunkte müssen eigenständig prüfen.

Ziel (Soll-Stand, aus Kundensicht)

  • Admin-Funktionen sind nur nach sicherer Anmeldung erreichbar.
  • Der geheime Admin-Key verbleibt ausschließlich auf dem Server/Backend und wird nicht in Frontend-Code, Images oder öffentlich zugängliche Dateien geschrieben.
  • Frontend kommuniziert nach Anmeldung mit dem Backend, ohne je den Admin-Key im Browser zu speichern.

Anforderungen (aus Sicht des Auftraggebers, umsetzbar durch eine KI)

  • Authentifizierung:

    • Einführung eines serverseitigen Login-Flows für Admins (Session-Cookies, HttpOnly, Secure, SameSite).
    • Nach erfolgreicher Anmeldung erhält der Admin-Browser ein HttpOnly-Cookie; dieses Cookie erlaubt Zugriff auf geschützte /api/admin/*-Endpoints.
    • Backend validiert alle /api/admin/*-Requests anhand der Session; nur dann wird mit dem internen ADMIN_API_KEY gearbeitet.
  • Secrets & Build:

    • Keine Secrets (z. B. ADMIN_API_KEY) im Frontend-Quellcode, in frontend/.env, in env-config.js oder in gebauten Bundles.
    • docker/prod/docker-compose.yml darf ADMIN_API_KEY nur dem backend-Service bereitstellen; keine Weitergabe an frontend.
    • Dockerfile des Frontends darf keine Produktion-.env kopieren, die Secrets enthält.
  • Betrieb & Infrastruktur:

    • Bestehende htpasswd-Absicherung der Admin-UI kann beibehalten werden als zusätzliche Hürde, ist aber nicht die einzige Schutzmaßnahme.
    • Empfehlung: ADMIN_API_KEY über sichere Secret-Mechanismen bereitstellen (CI/CD secret store, Docker Secrets, Swarm/K8s Secrets) — dies ist ein Hinweis, keine Pflichtanweisung.

Akzeptanzkriterien (klar messbar, für Tests durch eine KI/Dev)

  • Funktional:

    • Unauthentifizierte Requests an /api/admin/* erhalten 403 Forbidden.
    • Admin-Login-Endpoint existiert und setzt ein HttpOnly-Cookie; angemeldete Admins erreichen /api/admin/* erfolgreich.
  • Artefakte / Repo:

    • frontend-Bundle (der gebaute build/-Ordner) enthält nicht den Wert von ADMIN_API_KEY (automatischer Scan: kein Vorkommen des Key-Strings).
    • frontend/.env enthält keine REACT_APP_ADMIN_API_KEY-Zeile in Produktion; docker/prod/docker-compose.yml enthält keine Weitergabe des Keys an frontend.
  • Sicherheit & Ops:

    • Dokumentation: In AUTHENTICATION.md und in dieser Feature-Request-Datei wird der neue Login-Flow und Hinweis zum Secret-Handling vermerkt.
    • Dokumentation: In AUTHENTICATION.md und in dieser Feature-Request-Datei wird der neue Login-Flow und Hinweis zum Secret-Handling vermerkt.
    • Falls ein Key im Git-Verlauf existierte, ist die Rotation des Admin-Keys als Handlungsempfehlung dokumentiert.
    • Falls frontend/.env oder ein Admin-Key jemals in das Repository gelangt ist: Scannt die Git-History und entfernt das Secret aus der History, danach rotiert den Key. Empfohlene Tools/Schritte (kurz):
      • Finden: git log --all -S 'part-of-key' oder git grep -n "REACT_APP_ADMIN_API_KEY" $(git rev-list --all) oder nutzen truffleHog/git-secrets.
      • Entfernen aus History: git-filter-repo oder bfg-repo-cleaner (z.B. bfg --replace-text passwords.txt --no-blob-protection) — danach Force-Push in ein neues Remote (Achtung: Auswirkungen auf Contributors).
      • Key-Rotation: Erzeuge neuen ADMIN_API_KEY, setze ihn in der sicheren Backend-Umgebung (CI/CD secrets / Docker secret), redeploye Backend.
      • Hinweis: Diese Schritte sind invasiv für die Git-History; koordinieren mit Team bevor Ausführung.

Nicht-funktionale Anforderungen

  • Use Session-Cookies: Cookies müssen HttpOnly, Secure und SameSite=Strict (oder Lax falls nötig) gesetzt werden.
  • CSRF-Schutz: Bei Cookie-basierten Sessions muss ein CSRF-Schutzmechanismus vorhanden sein (z. B. double-submit-token oder CSRF-Header). Hinweis: CSRF-Mechanik ist zu implementieren, aber detaillierte Schritte sind nicht Teil dieses Requests.
  • Kompatibilität: Änderungen dürfen Entwickler-Workflows nicht unnötig blockieren; Dev-Mode-Patterns (runtime env-config.js in docker/dev) können bestehen bleiben, jedoch klar getrennt von Prod.

Hinweise für die implementierende KI / das Dev-Team (kontextbezogen)

  • Aktueller Code-Pfade von Relevanz:

    • frontend/src/services/adminApi.js — liest aktuell process.env.REACT_APP_ADMIN_API_KEY und setzt den Bearer-Token clientseitig.
    • frontend/.env — enthält aktuell REACT_APP_ADMIN_API_KEY.
    • docker/prod/docker-compose.yml — injiziert REACT_APP_ADMIN_API_KEY=${ADMIN_API_KEY} in frontend.
    • docker/prod/frontend/Dockerfile — kopiert docker/prod/frontend/config/.env in das Image und führt env.sh aus, das env-config.js erzeugt (window._env_).
    • docker/prod/frontend/config/env.sh — generiert zur Laufzeit env-config.js aus .env.
    • docker/prod/frontend/config/htpasswd — existierender Schutz der Admin-UI via nginx.
  • Erwartung an eine KI-Implementierung:

    • Verstehe die Codebasis (insbesondere frontend/src/* und backend/src/*) und identifiziere alle Stellen, die REACT_APP_ADMIN_API_KEY oder ADMIN_API_KEY verwenden oder weiterreichen.
    • Entferne clientseitige Verwendung des Admin-Keys; ersetze Aufrufe an Admin-API so, dass sie serverseitig autorisiert werden (Session-Check).
    • Verifiziere durch automatische Tests (Integrationstest oder API-Call) dass /api/admin/* ohne Session abgewiesen wird und mit Session funktioniert.

Was der Auftraggeber (Ich) erwartet — kurz und klar

  • Die Admin-Funktionen sind nur nach Anmeldung verfügbar.
  • Keine Admin-Secrets gelangen in Frontend-Bundles, Images oder öffentlich zugängliche Dateien.
  • Der existierende htpasswd-Schutz darf bestehen bleiben, ist aber nicht alleinige Sicherheitsmaßnahme.

Abnahmekriterien (für das Review durch den Auftraggeber)

  • Manuelle Überprüfung: Versuch, Admin-Endpoints ohne Login aufzurufen → 403.
  • Build-Review: gebaute Frontend-Dateien enthalten keinen Admin-Key.
  • Dokumentation aktualisiert (AUTHENTICATION.md weist auf neue Session-Flow hin).

Offene Fragen / Optionen (für Entwickler/KI) — Empfehlung und Umsetzungsdetails

  • Session-Store (empfohlen: SQLite für Single-Host, Redis für Skalierung)

    • Empfehlung für diese App: SQLite / file-basierter Session-Store (einfach zu betreiben, keine zusätzliche Infrastruktur).
    • Umsetzung (Express): benutze express-session + connect-sqlite3 (oder better-sqlite3 backend). Konfiguration:
      • Session-Cookie: HttpOnly, Secure (Prod), SameSite=Strict (oder Lax wenn externe callbacks nötig), maxAge angemessen (z. B. 8h).
      • Session-Secret aus sicherer Quelle (Backend ADMIN_SESSION_SECRET), nicht im Repo.
    • Skalierung: falls Cluster/Multiple hosts geplant, wechsle zu Redis (z.B. connect-redis) und setze Redis via Docker/K8s Secret.
  • CSRF-Mechanik (empfohlen: session-bound CSRF token + Header)

    • Empfehlung: Implementiere einen session-gebundenen CSRF-Token. Ablauf:
      1. Bei Login: generiere req.session.csrfToken = randomHex() auf dem Server.
      2. Exponiere Endpoint GET /auth/csrf-token (nur für eingeloggte Sessions), der das Token im JSON zurückgibt.
      3. Frontend ruft /auth/csrf-token nach Login (credentials: 'include') und speichert Token im JS-Scope.
      4. Bei state-changing Requests sendet Frontend X-CSRF-Token: <token> Header.
      5. Server-Middleware vergleicht Header mit req.session.csrfToken und verwirft bei Mismatch (403).
    • Vorteil: HttpOnly-Session-Cookie bleibt geschützt; Token ist an Session gebunden.
    • Alternative (schnell): Double-submit cookie (weniger robust, Token in non-HttpOnly cookie + Header); nur als kurzfristige Übergangslösung.
  • Entfernen von Admin-Key aus Frontend/Build (konkrete Änderungen)

    • frontend/src/services/adminApi.js: entferne Nutzung von process.env.REACT_APP_ADMIN_API_KEY. Passe adminRequest so an, dass credentials: 'include' verwendet wird und kein Bearer-Token gesetzt wird.
    • docker/prod/docker-compose.yml: lösche die Zeile - REACT_APP_ADMIN_API_KEY=${ADMIN_API_KEY} unter frontend.
    • docker/prod/frontend/Dockerfile: entferne COPY docker/prod/frontend/config/.env ./.env (oder stelle sicher, dass die Datei keine Secrets enthält). Vermeide Prod .env Kopie in Image.
    • docker/prod/frontend/config/env.sh: darf nur non-sensitive Werte (z. B. API_URL, APP_VERSION) schreiben; dokumentiere welche Keys erlaubt sind.
  • Secrets-Delivery / Deployment

    • Backend-Secret ADMIN_API_KEY und ADMIN_SESSION_SECRET via CI/CD Secret Store oder Docker Secrets bereitstellen.
    • Beispiel (Docker secret): erstelle Secret im Swarm/K8s und referenziere es nur im backend-Service.
  • Tests / Abnahme (automatisierbar)

    • Integrationstest 1: GET /api/admin/* ohne Session → 403.
    • Integrationstest 2: Login-Flow → setze Session-Cookie → POST /api/admin/* mit gültigem X-CSRF-Token200.
    • Build-Scan: nach Build grep -R "<part-of-admin-key>" build/ oder rg REACT_APP_ADMIN_API_KEY build/ → kein Treffer.
  • Key-Leak Reaktion

    • Falls Key geleakt: sofort Key-Rotation, dokumentiere wie (generate new key, update backend secret, redeploy), und führe Git-History-Check wie in Abschnitt oben durch.
  • Operationale Hinweise

    • Behalte htpasswd-Schutz für Admin-UI als zusätzliche Zugangskontrolle, nicht als einzige Schutzmaßnahme.
    • Dokumentiere alle Änderungen in AUTHENTICATION.md und in Release-Notes.

Diese Empfehlungen sind aus Sicht einer sicheren, pragmatischen Umsetzung gewählt: sie minimieren Infrastruktur-Overhead (SQLite) und bieten klare, testbare CSRF/SessionMechaniken. Eine implementierende KI soll die obenstehenden Punkte in konkreten CodePatches umsetzen und automatisierte Tests hinzufügen.

Kontakt / Kontext

  • Repo: Project-Image-Uploader — bestehende Struktur docker/prod, frontend/, backend/ beachten.
  • Hinweis: Diese Feature-Request-Datei benennt Missstände und Anforderungen; sie erwartet, dass ein Entwickler oder eine KI die Implementierung auf Code-Ebene vornimmt.