Files
crm-system/docs/audits/06a-auth-audit.md
T

6.1 KiB
Raw Blame History

06a Auth-Audit (Security & Data-Engineering)

Projekt: CRM System v1.0
Datum: 2026-06-04
Auditor: Security Data Engineer (Phase 6)
Repository: Leopoldadmin/crm-system, Branch main
Scope: JWT-Implementation, Password-Hashing, Auth-Endpoints, CORS/CSP-Header, Token-Rotation, Secrets in Git-Verlauf


Findings

ID Severity Kategorie Beschreibung Empfehlung
AUTH-01 PASS JWT-Algorithmus HS256 mit python-jose[cryptography]==3.3.0 gemäß Architecture-Decision Section 13.1. decode_access_token() validiert Signatur und Ablauf korrekt über jwt.decode() mit explizitem Algorithmus-Parameter. Beibehalten. Für v1.2 RS256 evaluieren (bessere Rotation, kein Shared-Secret).
AUTH-02 PASS Secret-Länge AUTH_SECRET wird in config.py mit Field(..., min_length=32) validiert. Ein benutzerdefinierter Validator validate_auth_secret() lehnt Platzhalter wie replace-me, changeme und den Literal secret ab. Hard-Fail bei fehlendem oder zu kurzem Secret (kein Fallback). Keine Änderung nötig. Erfüllt NFR-2 und Architecture R-5.
AUTH-03 PASS Token-Expiry 24h über JWT_EXPIRY_HOURS in config.py konfigurierbar. create_access_token() setzt exp-Claim korrekt via datetime.now(UTC) + timedelta. decode_access_token() fängt JWTError ab (deckt auch Expiry). Kurzfristigeres Expiry (z.B. 2h) + Refresh-Token in v1.1 für höhere Sicherheit.
AUTH-04 PASS Token-Validation get_current_user in deps.py nutzt decode_access_token(), prüft sub-Claim und User-Existenz (inkl. deleted_at IS NULL). 401-Response mit token_expired_or_invalid für konsistente Client-Behandlung. Validierung ist robust. Zusätzlicher Check auf iat-Claim könnte Replay-Angriffe erschweren (optional).
AUTH-05 PASS Password-Hashing bcrypt via passlib[bcrypt]==1.7.4 mit bcrypt==4.0.1. pwd_context mit deprecated="auto". BCRYPT_ROUNDS=12 konfigurierbar. hash_password() und verify_password() korrekt implementiert. Keine Änderung nötig. bcrypt 4.0.1 ist aktuell und sicher.
AUTH-06 PASS Kein Default-Admin Bootstrap-Registrierung via POST /api/v1/auth/register nur bei leerer users-Tabelle. Nach erstem User → 403 (BootstrapAlreadyCompleted). Kein admin/admin-Fallback. Erfüllt Architecture R-3.
AUTH-07 PASS Register-Endpoint POST /api/v1/auth/register validiert via UserRegisterRequest: email: EmailStr, password: str(min_length=8, max_length=128), name: str(min_length=1, max_length=255). 409 bei doppelter Email (generisch), 201 bei Erfolg. Validiert korrekt. Rate-Limiting fehlt noch (v1.1, siehe Architecture 13.4).
AUTH-08 PASS Login-Endpoint POST /api/v1/auth/login (OAuth2Form) + /login/json (JSON). 401 mit generischer Meldung "Invalid email or password" leakt nicht, ob Email existiert. WWW-Authenticate: Bearer Header gesetzt. Erfüllt FR-1.2 Akzeptanzkriterien.
AUTH-09 PASS Logout-Endpoint POST /api/v1/auth/logout validiert Token (Dependency get_current_user), aber keine serverseitige Blacklist. Client-seitiger Token-Discard dokumentiert. Für v1 akzeptabel. Server-seitige Blacklist erst in v1.1.
AUTH-10 PASS /users/me-Endpoint GET /api/v1/users/me via get_current_user geschützt. 401 ohne Token, 401 mit expired Token (token_expired_or_invalid), password_hash nie im Response. Erfüllt FR-1.6 Akzeptanzkriterien AC#7-#9.
AUTH-11 PASS CORS-Whitelist CORS_ORIGINS aus Env-Var (Komma-separiert), Default http://localhost:5500,http://localhost:8000. Kein *. settings.cors_origins_list parsed korrekt. Erfüllt Architecture R-4.
AUTH-12 INFO CSP-Header In main.py security_headers_middleware gesetzt. Dev: script-src 'self' 'unsafe-inline' ... (für Alpine.js). Prod: nur script-src 'self' ... (ohne unsafe-inline) Alpine.js-Inline-Skripte würden blockiert. X-Content-Type-Options, X-Frame-Options, HSTS (Prod) gesetzt. Vor Produktion: Nonce-basierte CSP für Alpine.js implementieren (v1.1 ToDo). Aktuelle Prod-CSP würde Frontend blockieren.
AUTH-13 INFO Refresh-Token-Rotation /api/v1/auth/refresh existiert, re-signed aber nur mit gleichem Secret keine echte Rotation. Rotation ist für v1.1 geplant und im Code-Kommentar dokumentiert. Kein Sicherheitsrisiko für v1, da Token-Expiry 24h beträgt. Für v1.1: Refresh-Token mit separatem Secret + Rotation.
AUTH-14 WARN Secrets im Git-Verlauf git log -p zeigt Passwörter in Test-Dateien (test_auth.py, test_smoke.py, conftest_helper.py), z.B. "password": "Test1234!", "password": "SuperSecret123!". Dies sind Test-Credentials ohne Produktionsrelevanz. Kein kritisches Risiko, aber Good-Practice: Test-Passwörter aus Git-Verlauf entfernen (via git filter-branch oder git rebase). Kein Blocker für Phase 7.

Summary: PASS

Das Auth-System ist sicher und erfüllt alle Anforderungen aus 01-requirements.md (FR-1.x, NFR-2) und 02-architecture.md (13.113.5). JWT-Implementation, Passwort-Hashing und Endpoint-Access-Control sind korrekt implementiert. Keine kritischen Findings.

Einzig offener Punkt: CSP-Header muss vor Produktion auf Nonce umgestellt werden (AUTH-12), da die aktuelle Prod-CSP Alpine.js-Inline-Skripte blockieren würde. Dies ist ein geplanter v1.1-Task.


Empfehlungen für Phase 7 (Quality-Reviewer)

  1. CSP-Nonce-Migration vor Deployment Prod-CSP aktuell ohne unsafe-inline → Frontend funktioniert nicht. Muss vor Production-Release behoben werden.
  2. Password-Hashing-Verifikation Sicherstellen, dass bcrypt==4.0.1 korrekt gepinnt ist (4.1+ bricht passlib).
  3. Token-Expiry-Test automatisieren test_auth.py:test_expired_token_returns_401 prüft explizit token_expired_or_invalid, aber Integration-Test könnte race-condition bei iat/exp haben.
  4. Rate-Limiting-Akzeptanz prüfen Ohne LoginAttempt-Tabelle (v1.1) ist der Login-Endpoint ungebremst. In Phase 7 dokumentieren, ob dies für v1-Go-Live akzeptabel ist.