# STEP 4.3 – HARDCODED SECRET KEY ENTFERNT (LÜCKE #19) # Status: ABGESCHLOSSEN --- ## Fundstellen | Datei | Zeile | Problem | |-------|-------|---------| | `workforce_planner/config.py` | 18 | `SECRET_KEY = os.getenv("WP_SECRET_KEY", "dev-secret-change-in-production")` – Hardcoded Fallback-Secret | | `workforce_planner/api/auth.py` | 35, 47 | Konsument: JWT encode/decode mit `SECRET_KEY` | Keine weiteren hardcoded Secrets gefunden: - `basis14.py`: OPENAI_API_KEY kommt aus ENV, kein Fallback - `basis14.py`: MEDWORK_API_TOKEN kommt aus ENV oder Datei, kein hardcoded Wert - `aza_email_config.json`: Passwörter im Klartext (separates GAP, nicht in diesem Schritt) --- ## Neue ENV-Variablen | Variable | Pflicht | Beschreibung | |----------|---------|-------------| | `AZA_SECRET_KEY` | Ja (ausser DEV) | JWT-Signing-Key, min. 32 Zeichen | | `AZA_ENV` | Nein | `dev` = erlaubt auto-generierten Key | Alte Variable `WP_SECRET_KEY` wird **nicht mehr verwendet**. --- ## Implementierung ### workforce_planner/config.py (komplett neu) **ALT:** ```python SECRET_KEY = os.getenv("WP_SECRET_KEY", "dev-secret-change-in-production") ``` **NEU:** ```python def _load_secret_key() -> str: key = os.getenv("AZA_SECRET_KEY", "").strip() env_mode = os.getenv("AZA_ENV", "").strip().lower() # Kein Key: DEV auto-gen oder Fail-Start if not key: if env_mode == "dev": return secrets.token_hex(64) # temporär, nur diese Session sys.exit(1) # Fehlermeldung + Exit # Zu kurz (< 32 Zeichen): Fail-Start if len(key) < 32: sys.exit(1) # Triviales Muster: Fail-Start for pattern in ("dev", "test", "password", "secret", ...): if key.startswith(pattern) und nicht-alphanumerisches Folgezeichen: sys.exit(1) return key SECRET_KEY = _load_secret_key() ``` ### workforce_planner/api/auth.py Keine Änderung nötig – importiert weiterhin `SECRET_KEY` aus `config.py`. Die Variable hat denselben Namen, der Wert kommt jetzt validiert aus ENV. --- ## Fail-Start Verhalten | Szenario | Ergebnis | |----------|----------| | Kein `AZA_SECRET_KEY`, kein `AZA_ENV=dev` | Exit 1 + Fehlermeldung | | `AZA_ENV=dev`, kein Key | OK (auto-gen, Warnung auf stderr) | | Key < 32 Zeichen | Exit 1 + "zu kurz" | | Key mit trivialem Muster ("dev-...", "test-...", "password...") | Exit 1 + "triviales Muster" | | Gültiger Key >= 32 Zeichen | OK | | Nur `WP_SECRET_KEY` gesetzt (alt) | Exit 1 (wird ignoriert) | --- ## Testkommandos + erwartete Outputs ### 1. Fail-Start (kein Key) ```bash python -c "from workforce_planner.config import SECRET_KEY" # → Exit 1: FEHLER: AZA_SECRET_KEY ist nicht gesetzt. ``` ### 2. DEV-Modus (auto-gen) ```bash AZA_ENV=dev python -c "from workforce_planner.config import SECRET_KEY; print(len(SECRET_KEY))" # → 128 (hex-encoded 64 Bytes) + Warnung auf stderr ``` ### 3. Zu kurzer Key ```bash AZA_SECRET_KEY=short123 python -c "from workforce_planner.config import SECRET_KEY" # → Exit 1: FEHLER: AZA_SECRET_KEY ist zu kurz (8 Zeichen, Minimum: 32) ``` ### 4. Triviales Muster ```bash AZA_SECRET_KEY="test-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" python -c "from workforce_planner.config import SECRET_KEY" # → Exit 1: FEHLER: AZA_SECRET_KEY enthält triviales Muster ('test') ``` ### 5. Gültiger Key ```bash AZA_SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(64))") python -c "from workforce_planner.config import SECRET_KEY; print('OK:', len(SECRET_KEY), 'chars')" # → OK: 128 chars ``` --- ## Testergebnis (automatisiert) ``` 12/12 PASS, 0/12 FAIL GESAMTBEWERTUNG: PASS ``` | Test | Ergebnis | |------|----------| | Fail-Start ohne Key | PASS | | Fehlermeldung enthält 'AZA_SECRET_KEY' | PASS | | DEV auto-gen Key | PASS | | Key geladen (128 chars) | PASS | | Warnung ausgegeben | PASS | | Fail-Start kurzer Key | PASS | | Fehlermeldung 'zu kurz' | PASS | | Fail-Start alter Fallback | PASS | | Fail-Start triviales Pattern (lang) | PASS | | Start mit gültigem Key | PASS | | Key geladen (64 chars) | PASS | | WP_SECRET_KEY allein -> Fail-Start | PASS | --- ## Session/Token-Invalidierung **Impact:** Bestehende JWT-Tokens (HS256), die mit dem alten Fallback-Key `"dev-secret-change-in-production"` signiert wurden, werden nach dem Setzen eines neuen `AZA_SECRET_KEY` **ungültig**. - Betroffene Nutzer müssen sich **einmalig neu anmelden**. - Dies betrifft nur den `workforce_planner` (Arbeitsplan-Modul). - `basis14.py` verwendet keine JWT-Tokens (nur lokales Passwort-Hashing). - **Keine komplexe Rotation** implementiert (nicht im Scope dieses Schrittes). - Bei Produktivbetrieb: Key einmalig setzen und dauerhaft beibehalten. --- ## Geänderte Dateien | Datei | Änderung | |-------|----------| | `workforce_planner/config.py` | Hardcoded Fallback entfernt, `_load_secret_key()` mit Validierung + Fail-Start implementiert |