163 lines
4.8 KiB
Markdown
163 lines
4.8 KiB
Markdown
|
|
# 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 |
|