# STEP 6 – TOTP-ZWEI-FAKTOR-AUTHENTIFIZIERUNG # Status: ABGESCHLOSSEN --- ## Architektur ### Komponenten ``` aza_totp.py – Zentrales TOTP-Modul (RFC 6238) basis14.py – GUI-Integration (Login, Profil, Setup) pyotp (2.9.0) – TOTP-Implementierung (RFC 6238 kompatibel) qrcode[pil] (8.2) – QR-Code-Generierung ``` ### Flow ``` Login-Dialog │ ▼ Passwort prüfen ──► FAIL → Fehlermeldung │ ▼ OK 2FA aktiv? ──► NEIN + 2FA_REQUIRED → Erzwinge 2FA-Setup │ │ ▼ JA ▼ TOTP-Dialog ──────────────────────── 2FA-Setup-Dialog │ │ ▼ ▼ Code prüfen QR-Code scannen │ Code validieren ├─ TOTP OK → Login │ ├─ Backup-Code OK → Login Backup-Codes anzeigen └─ FAIL → Fehlermeldung │ 2FA aktiviert ``` --- ## Datenmodell ### Profil (kg_diktat_user_profile.json) ```json { "name": "Dr. Beispiel", "specialty": "Dermatologie", "clinic": "Praxis XY", "password_hash": "$2b$12$...", "totp_active": true, "totp_secret_enc": "Base64-verschlüsselter-TOTP-Secret", "backup_codes": [ "sha256-hash-code-1", "sha256-hash-code-2", "", "sha256-hash-code-4", ... ] } ``` | Feld | Typ | Beschreibung | |------|-----|-------------| | `totp_active` | boolean | 2FA aktiviert/deaktiviert | | `totp_secret_enc` | string | TOTP-Secret, XOR-verschlüsselt mit Passwort-Hash, Base64 | | `backup_codes` | string[] | SHA-256-Hashes der Backup-Codes; leer = verbraucht | --- ## TOTP-Parameter | Parameter | Wert | |-----------|------| | Algorithmus | TOTP (RFC 6238) | | Hash | SHA-1 (Standard per RFC) | | Zeitschritt | 30 Sekunden | | Code-Länge | 6 Ziffern | | Gültigkeitsfenster | +-1 (90 Sekunden total) | | Secret-Länge | 160 bit (32 Base32-Zeichen) | --- ## Sicherheitsmassnahmen ### Secret-Verschlüsselung - TOTP-Secret wird NIE im Klartext gespeichert - Verschlüsselung: XOR mit SHA-256(Benutzer-Passwort) - Encoding: Base64 - Entschlüsselung nur mit korrektem Passwort möglich - Bei falschem Passwort: leerer String (kein Crash) ### Backup-Codes - 8 Einmal-Codes (8 Zeichen, hex, uppercase) - Gespeichert als SHA-256-Hashes - Verbrauchte Codes werden zu leerem String - Case-insensitive Eingabe - HMAC-basierter Vergleich (timing-safe) ### Rate-Limiting - Max. 5 TOTP-Versuche pro 5 Minuten pro Benutzer - Nach Erreichen: auch korrekter Code wird blockiert - Im RAM gehalten (Reset bei Neustart) --- ## ENV-Variablen | Variable | Default | Beschreibung | |----------|---------|-------------| | `AZA_2FA_ENABLED` | `1` | 2FA-Feature verfügbar | | `AZA_2FA_REQUIRED` | `0` | 2FA ist Pflicht für alle Benutzer | ### Verhalten | AZA_2FA_ENABLED | AZA_2FA_REQUIRED | Ergebnis | |:---:|:---:|---| | 0 | 0 | 2FA komplett deaktiviert, kein Button im Profil | | 1 | 0 | 2FA optional, Aktivierung im Profil | | 1 | 1 | 2FA Pflicht, Setup nach erstem Login erzwungen | | 0 | 1 | Feature deaktiviert, Required ignoriert | --- ## Recovery - Wiederherstellung NUR über Backup-Codes - Kein Admin-Reset ohne Prüfung - Kein E-Mail-Recovery - Backup-Codes werden nach 2FA-Aktivierung einmalig angezeigt - Benutzer muss Codes selbst sichern (Kopieren/Ausdrucken) - Bei aufgebrauchten Backup-Codes: Profil-Datei manuell bereinigen (Admin-Eingriff) --- ## Geänderte / Neue Dateien | Datei | Änderung | |-------|----------| | `aza_totp.py` | NEU: Zentrales TOTP-Modul | | `basis14.py` | Import: pyotp, qrcode, PIL.ImageTk, aza_totp | | `basis14.py` | `_show_password_login()`: 2FA-Check nach Passwort | | `basis14.py` | `_show_totp_login()`: NEU – TOTP-Code-Dialog | | `basis14.py` | `_show_2fa_setup()`: NEU – QR-Code + Erst-Validierung | | `basis14.py` | `_show_backup_codes()`: NEU – Backup-Code-Anzeige | | `basis14.py` | `_show_profile_editor()`: 2FA-Aktivierung/Deaktivierung | --- ## Dependencies ``` pyotp==2.9.0 # TOTP RFC 6238 qrcode[pil]==8.2 # QR-Code-Generierung ``` --- ## Testergebnis ``` 24/24 PASS, 0/24 FAIL GESAMTBEWERTUNG: PASS ``` | Test | Ergebnis | |------|----------| | Secret-Generierung (Base32, 32 Zeichen) | PASS | | Secrets sind einzigartig | PASS | | Provisioning URI (otpauth://) | PASS | | URI enthält Issuer | PASS | | URI enthält Secret | PASS | | Korrekter Code akzeptiert | PASS | | Falscher Code abgelehnt | PASS | | Leerer Code abgelehnt | PASS | | Abgelaufener Code (90s) abgelehnt | PASS | | 8 Backup-Codes generiert | PASS | | Code-Format (8 hex chars) | PASS | | Codes einzigartig | PASS | | Hashes SHA-256 | PASS | | Backup-Code Lookup | PASS | | Falscher Backup-Code abgelehnt | PASS | | Case-insensitive | PASS | | Einmalnutzung | PASS | | Invalidierter Code abgelehnt | PASS | | Secret-Verschlüsselung | PASS | | Verschlüsselt != Klartext | PASS | | Entschlüsselung korrekt | PASS | | Falsches PW = falsches Secret | PASS | | Rate-Limit greift nach 5 Versuchen | PASS | | Rate-Limit blockiert auch korrekte Codes | PASS | --- ## Risiken 1. **XOR-Verschlüsselung:** Für eine Desktop-App akzeptabel, aber kryptographisch schwächer als AES. Upgrade auf Fernet/AES bei Bedarf. 2. **Secret an Passwort gebunden:** Bei Passwortänderung muss das TOTP-Secret neu verschlüsselt werden. Aktuell nicht automatisch – 2FA muss nach Passwortänderung neu eingerichtet werden. 3. **Rate-Limit im RAM:** Wird bei Neustart zurückgesetzt. Für Desktop-App akzeptabel (kein Netzwerk-Angriff möglich). 4. **Kein TOTP für workforce_planner:** Die 2FA ist nur im Desktop-Client (basis14.py) implementiert. Der workforce_planner (Web-API) hat noch keine 2FA-Integration. 5. **SHA-1 in TOTP:** RFC 6238 spezifiziert SHA-1 als Standard. Alle gängigen Authenticator-Apps (Google, Microsoft, Authy) erwarten SHA-1.