5.9 KiB
5.9 KiB
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)
{
"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
-
XOR-Verschlüsselung: Für eine Desktop-App akzeptabel, aber kryptographisch schwächer als AES. Upgrade auf Fernet/AES bei Bedarf.
-
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.
-
Rate-Limit im RAM: Wird bei Neustart zurückgesetzt. Für Desktop-App akzeptabel (kein Netzwerk-Angriff möglich).
-
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.
-
SHA-1 in TOTP: RFC 6238 spezifiziert SHA-1 als Standard. Alle gängigen Authenticator-Apps (Google, Microsoft, Authy) erwarten SHA-1.