update
This commit is contained in:
525
backup 24.2.26 - Kopie/security/audit/AUDIT_READINESS_2026-02.md
Normal file
525
backup 24.2.26 - Kopie/security/audit/AUDIT_READINESS_2026-02.md
Normal file
@@ -0,0 +1,525 @@
|
||||
# AUDIT-READINESS ASSESSMENT
|
||||
# AZA / MedWork – Februar 2026
|
||||
|
||||
**Assessor:** Internes Security-Team (automatisiert)
|
||||
**Datum:** 2026-02-22
|
||||
**Scope:** AZA/MedWork Gesamtsystem
|
||||
**Datenklassifikation:** Besonders schützenswerte Personendaten (Gesundheitsdaten)
|
||||
**Rechtsrahmen:** DSG (Schweiz, rev. 2023), DSGVO (ergänzend), HIN-Referenzniveau
|
||||
**Referenzdokumente:** TOMs_AZA_MedWork.md, GAP-Analyse (STEP 3), Handovers STEP 4.1–6
|
||||
|
||||
---
|
||||
|
||||
## 1. Technische Sicherheit
|
||||
|
||||
---
|
||||
|
||||
### 1.1 Transport Security
|
||||
|
||||
#### Status: YELLOW
|
||||
|
||||
#### Begründung:
|
||||
- TLS >= 1.2 für alle Backend-Services erzwungen (STEP 4.1, verifiziert in 4.1a)
|
||||
- Starke Cipher Suites mit PFS (ECDHE+AESGCM, CHACHA20)
|
||||
- HTTP wird komplett abgelehnt (kein Redirect, Connection Refused)
|
||||
- Fail-Start bei fehlenden Zertifikaten
|
||||
- **ABER:** Nur Self-Signed-Zertifikate für Entwicklung vorhanden
|
||||
- **ABER:** SMTP STARTTLS ist opportunistisch (nicht erzwungen)
|
||||
- **ABER:** Keine Ende-zu-Ende-Verschlüsselung für E-Mails
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Nein, kein harter Blocker. TLS ist implementiert und verifiziert.
|
||||
- Produktivzertifikate (Let's Encrypt / CA-signiert) müssen vor Go-Live beschafft werden.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Produktiv-Zertifikate beschaffen (Let's Encrypt / ACME)
|
||||
2. SMTP-STARTTLS-Erzwingung implementieren (Fehler bei fehlgeschlagenem TLS)
|
||||
3. S/MIME oder Gateway-Verschlüsselung für medizinische E-Mails evaluieren
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Authentifizierung
|
||||
|
||||
#### Status: YELLOW
|
||||
|
||||
#### Begründung:
|
||||
- Passwort-Login mit bcrypt (cost=12, Salt eingebettet) – STEP 4.2
|
||||
- Legacy-SHA-256-Migration transparent implementiert und verifiziert – STEP 4.2a
|
||||
- TOTP-2FA implementiert (RFC 6238, optional/erzwingbar via ENV) – STEP 6
|
||||
- Backup-Codes (8 Einmal-Codes, SHA-256-gehasht) vorhanden
|
||||
- Rate-Limiting auf TOTP-Versuche (5/5min)
|
||||
- RBAC im Workforce Planner (3 Rollen)
|
||||
- **ABER:** 2FA nur im Desktop-Client, nicht im Workforce Planner (Web-API)
|
||||
- **ABER:** Kein SSO (SAML/OAuth/OIDC)
|
||||
- **ABER:** Keine Identitätsprüfung (Videoidentifikation o.ä.)
|
||||
- **ABER:** Minimale Passwortanforderung (4 Zeichen) ist zu schwach
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja, teilweise. Für medizinische Systeme wird 2FA in der Regel erwartet.
|
||||
Im Desktop-Client vorhanden, im Web-API fehlt sie.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Minimale Passwortlänge auf 8+ Zeichen erhöhen
|
||||
2. 2FA im Workforce Planner (Web-API) implementieren
|
||||
3. `AZA_2FA_REQUIRED=1` als Standard für Produktion setzen
|
||||
|
||||
---
|
||||
|
||||
### 1.3 Secrets Management
|
||||
|
||||
#### Status: GREEN
|
||||
|
||||
#### Begründung:
|
||||
- Hardcoded Secret Key entfernt – STEP 4.3
|
||||
- Zentrale Validierung: Mindestlänge 32 Zeichen, keine trivialen Muster
|
||||
- Fail-Start bei fehlendem oder schwachem Key
|
||||
- DEV-Modus mit auto-generiertem Key (explizit AZA_ENV=dev)
|
||||
- Klartext-Credentials aus Config entfernt – STEP 4.4
|
||||
- E-Mail-Passwörter nur via ENV-Variablen
|
||||
- Incident-Dokumentation für exponiertes Passwort erstellt – STEP 4.4a
|
||||
- TOTP-Secrets verschlüsselt gespeichert (nie Klartext auf Disk)
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Nein. Secret Management ist solide implementiert und dokumentiert.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Passwort beim Mail-Provider rotieren (INCIDENT offen)
|
||||
2. OS-Keychain-Integration evaluieren (Windows Credential Manager)
|
||||
3. Secret-Scanning in CI/CD-Pipeline einführen
|
||||
|
||||
---
|
||||
|
||||
### 1.4 2FA
|
||||
|
||||
#### Status: YELLOW
|
||||
|
||||
#### Begründung:
|
||||
- TOTP (RFC 6238) implementiert mit pyotp – STEP 6
|
||||
- QR-Code-Provisioning, Erst-Validierung, Backup-Codes
|
||||
- Rate-Limiting (5 Versuche / 5 Minuten)
|
||||
- ENV-Steuerung (AZA_2FA_ENABLED, AZA_2FA_REQUIRED)
|
||||
- **ABER:** Nur im Desktop-Client (basis14.py), nicht im Workforce Planner
|
||||
- **ABER:** Aktuell optional (AZA_2FA_REQUIRED=0)
|
||||
- **ABER:** Kein Hardware-Token-Support (FIDO2/WebAuthn)
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Teilweise. 2FA ist implementiert, aber nicht standardmässig erzwungen.
|
||||
Für HIN-Niveau wäre 2FA-Pflicht erforderlich.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. AZA_2FA_REQUIRED=1 als Produktiv-Standard
|
||||
2. 2FA im Workforce Planner nachrüsten
|
||||
3. FIDO2/WebAuthn als Upgrade-Option evaluieren
|
||||
|
||||
---
|
||||
|
||||
### 1.5 Logging & Monitoring
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Audit-Log-Modell existiert (AuditLog-Tabelle in workforce_planner)
|
||||
- log_action()-Funktion definiert
|
||||
- **ABER:** log_action() wird in keinem API-Endpunkt aufgerufen
|
||||
- **ABER:** Kein Logging fehlgeschlagener Anmeldeversuche
|
||||
- **ABER:** Kein zentrales Logging-Framework
|
||||
- **ABER:** Kein Monitoring, kein Alerting
|
||||
- **ABER:** Debug-Prints statt strukturiertem Logging
|
||||
- **ABER:** Kein SIEM
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja. Fehlende Nachvollziehbarkeit von Datenzugriffen ist ein Kernproblem
|
||||
für medizinische Systeme (DSG Art. 8, DSGVO Art. 5 Abs. 2).
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. log_action() in alle schreibenden API-Endpunkte integrieren
|
||||
2. Fehlgeschlagene Login-Versuche protokollieren
|
||||
3. Strukturiertes Logging (Python logging-Modul) einführen
|
||||
|
||||
---
|
||||
|
||||
## 2. Datenschutz
|
||||
|
||||
---
|
||||
|
||||
### 2.1 TOMs-Dokumentation
|
||||
|
||||
#### Status: GREEN
|
||||
|
||||
#### Begründung:
|
||||
- Vollständiges TOMs-Dokument erstellt (12 Bereiche) – STEP 5
|
||||
- Alle 12 DSG/DSGVO-Bereiche abgedeckt
|
||||
- Klare Trennung: implementiert vs. nicht implementiert
|
||||
- 35 Massnahmen implementiert, 62 offen
|
||||
- Referenzen auf technische Implementierungen vorhanden
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Nein. TOMs-Dokument ist vorhanden und audit-tauglich.
|
||||
Inhaltlich zeigt es aber viele offene Punkte auf.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. TOMs nach jedem Security-Schritt aktualisieren
|
||||
2. Versionierung/Changelog für TOMs einführen
|
||||
3. Verantwortlichkeiten pro TOM zuweisen
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Incident Management
|
||||
|
||||
#### Status: YELLOW
|
||||
|
||||
#### Begründung:
|
||||
- Ein Incident wurde formal dokumentiert (Credential Exposure) – STEP 4.4a
|
||||
- Checkliste mit Sofortmassnahmen und Folgeaktionen vorhanden
|
||||
- Security-Handovers für alle Schritte dokumentiert
|
||||
- **ABER:** Kein formaler Incident-Response-Plan
|
||||
- **ABER:** Keine Eskalationsstufen definiert
|
||||
- **ABER:** Keine Prozesse für DSG Art. 24 (Meldung an EDÖB innert 72h)
|
||||
- **ABER:** Passwort-Rotation (Incident-Empfehlung) noch offen
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja, teilweise. Das DSG verlangt eine Meldefähigkeit innert 72h.
|
||||
Ohne definierten Prozess ist diese nicht gewährleistet.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Incident-Response-Plan erstellen (Rollen, Eskalation, Fristen)
|
||||
2. Meldeprozess an EDÖB definieren (72h-Frist)
|
||||
3. Offene Incident-Massnahme abschliessen (Passwort-Rotation)
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Datenminimierung
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Keine formale Datenminimierungsstrategie
|
||||
- Patientendaten (KG) werden lokal ohne Löschfristen gespeichert
|
||||
- Keine Klassifikation der verarbeiteten Daten
|
||||
- Keine Prüfung, ob nur notwendige Daten erhoben werden
|
||||
- OpenAI API erhält potenziell Patientendaten (Transkription, KG-Generierung)
|
||||
ohne dokumentierte Notwendigkeitsprüfung
|
||||
- E-Mail-Inhalte werden vollständig lokal gecacht
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja. Datenminimierung ist ein Grundprinzip (DSG Art. 6 Abs. 2,
|
||||
DSGVO Art. 5 Abs. 1 lit. c). Ohne Nachweis ist ein Audit nicht bestehbar.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Datenverarbeitungsverzeichnis (Art. 12 DSG) erstellen
|
||||
2. Prüfen welche Daten an OpenAI gesendet werden (Anonymisierung?)
|
||||
3. Aufbewahrungsfristen für medizinische Daten definieren (kant. Recht: 10–20 Jahre)
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Löschkonzepte
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Kein Löschkonzept vorhanden
|
||||
- Keine automatische Datenlöschung
|
||||
- Keine Löschfunktion für Patientendaten
|
||||
- Keine Dokumentation der Aufbewahrungsfristen
|
||||
- Medizinische Daten: kantonale Aufbewahrungspflichten (typisch 10 Jahre)
|
||||
sind weder dokumentiert noch technisch unterstützt
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja. Recht auf Löschung (DSG Art. 32, DSGVO Art. 17) nicht umsetzbar.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Aufbewahrungsfristen pro Datentyp definieren
|
||||
2. Löschfunktion für Patientendaten implementieren
|
||||
3. Automatische Löschung nach Ablauf der Aufbewahrungsfrist
|
||||
|
||||
---
|
||||
|
||||
## 3. Organisation
|
||||
|
||||
---
|
||||
|
||||
### 3.1 Rollen & Verantwortlichkeiten
|
||||
|
||||
#### Status: YELLOW
|
||||
|
||||
#### Begründung:
|
||||
- RBAC im Workforce Planner (ADMIN, MANAGER, EMPLOYEE)
|
||||
- Rollen-basierte API-Zugriffskontrolle implementiert
|
||||
- Security-Projekt hat definierte Rollen (Security Engineer, Architekt, Inhaber)
|
||||
- **ABER:** Kein Datenschutzbeauftragter (DSB) benannt
|
||||
- **ABER:** Kein IT-Sicherheitsbeauftragter benannt
|
||||
- **ABER:** Keine Zugriffsmatrix für Patientendaten
|
||||
- **ABER:** Keine regelmässige Berechtigungsüberprüfung
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Teilweise. DSB-Benennung ist für medizinische Praxen in der Schweiz
|
||||
nicht zwingend (< 250 Mitarbeiter), aber empfohlen.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Datenschutz-Verantwortlichen benennen (auch wenn nicht DSB-pflichtig)
|
||||
2. Zugriffsmatrix für Patientendaten erstellen
|
||||
3. Regelmässige Berechtigungsüberprüfung einführen
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Schulung
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Keine Security-Awareness-Schulungen
|
||||
- Keine Dokumentation für Endbenutzer
|
||||
- Keine Schulung zum Umgang mit Patientendaten
|
||||
- Keine Phishing-Awareness
|
||||
- Keine Einweisungsprozedur
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja. Fehlende Mitarbeiterschulung ist ein Standard-Audit-Finding.
|
||||
DSG Art. 8 verlangt angemessene organisatorische Massnahmen.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Kurzschulung "Datenschutz für Praxismitarbeiter" erstellen (1 Seite)
|
||||
2. Benutzerhandbuch für AZA/MedWork mit Security-Hinweisen
|
||||
3. Jährliche Auffrischung dokumentieren
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Prozesse
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Kein Change-Management-Prozess
|
||||
- Kein Release-Management
|
||||
- Kein formaler Testprozess vor Produktiv-Updates
|
||||
- Kein Code-Review-Prozess
|
||||
- Kein Vulnerability-Disclosure-Prozess
|
||||
- Security-Projekt folgt strukturiertem Vorgehen (Steps), aber nicht formalisiert
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Teilweise. Fehlende Prozesse sind ein Risiko, aber für eine
|
||||
Einzelpraxis nicht zwingend auditrelevant.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Minimaler Change-Management-Prozess definieren
|
||||
2. Checkliste vor Produktiv-Deployment
|
||||
3. Regelmässige Dependency-Prüfung (pip-audit)
|
||||
|
||||
---
|
||||
|
||||
## 4. Betrieb
|
||||
|
||||
---
|
||||
|
||||
### 4.1 Backup & Recovery
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Nur manuelle Dateisystem-Backups (Ordner-Kopien)
|
||||
- Kein automatisiertes Backup
|
||||
- Kein Off-Site-Backup
|
||||
- Keine dokumentierte Recovery-Prozedur
|
||||
- Keine RTO/RPO definiert
|
||||
- Keine Wiederherstellungstests
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja. Verfügbarkeitskontrolle ist eine Grundanforderung (DSG Art. 8).
|
||||
Ohne Backup-Konzept für medizinische Daten nicht auditierbar.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Automatisiertes tägliches Backup einrichten
|
||||
2. RTO/RPO definieren (z.B. RPO=24h, RTO=4h)
|
||||
3. Quartalsmässige Wiederherstellungstests durchführen
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Patching
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Kein dokumentiertes Patch-Management
|
||||
- Keine automatische Dependency-Prüfung
|
||||
- Keine Update-Zyklen definiert
|
||||
- Kein Changelog
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Teilweise. Ohne Nachweis aktueller Dependencies besteht Risiko
|
||||
durch bekannte Schwachstellen.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. `pip-audit` einmalig ausführen und Ergebnis dokumentieren
|
||||
2. Monatlichen Dependency-Check einführen
|
||||
3. requirements.txt mit festen Versionen pflegen
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Monitoring
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Kein Health-Check-Monitoring
|
||||
- Kein Alerting bei Service-Ausfall
|
||||
- Kein Performance-Monitoring
|
||||
- Keine Anomalie-Erkennung
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Teilweise. Für eine Desktop-App weniger kritisch als für Cloud-Services.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Health-Check-Endpunkt für Backend-Services (bereits vorhanden: /health)
|
||||
2. Einfaches Uptime-Monitoring (z.B. Skript mit Alerting)
|
||||
3. Fehlgeschlagene Login-Versuche loggen und alarmieren
|
||||
|
||||
---
|
||||
|
||||
## 5. Recht
|
||||
|
||||
---
|
||||
|
||||
### 5.1 Informationspflichten
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Keine Datenschutzerklärung für Patienten
|
||||
- Keine Information über Datenverarbeitung beim Einsatz von OpenAI
|
||||
- Keine Einwilligungserklärung für KI-gestützte Verarbeitung
|
||||
- DSG Art. 19–21 (Informationspflicht) nicht umgesetzt
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja. Informationspflicht ist zwingend für medizinische Daten.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Datenschutzerklärung für Patienten erstellen
|
||||
2. Einwilligungserklärung für KI-Verarbeitung (OpenAI) erstellen
|
||||
3. Information über Datenübermittlung ins Ausland (OpenAI-Server)
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Auftragsverarbeitungsverträge (AV)
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Kein AV/DPA mit OpenAI dokumentiert
|
||||
- Kein AV/DPA mit Supabase dokumentiert
|
||||
- Kein AV/DPA mit E-Mail-Provider
|
||||
- Keine Prüfung der Datenverarbeitungsstandorte
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja. AV-Verträge sind gesetzlich vorgeschrieben bei Auftragsverarbeitung
|
||||
(DSG Art. 9, DSGVO Art. 28).
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. OpenAI DPA prüfen/abschliessen (existiert als Standard-DPA bei OpenAI)
|
||||
2. Supabase DPA prüfen/abschliessen
|
||||
3. Datenverarbeitungsstandorte dokumentieren (Schweiz/EU/US)
|
||||
|
||||
---
|
||||
|
||||
### 5.3 Privacy Policy / Einwilligungen
|
||||
|
||||
#### Status: RED
|
||||
|
||||
#### Begründung:
|
||||
- Keine Privacy Policy vorhanden
|
||||
- Keine Einwilligungsmechanismen in der Anwendung
|
||||
- Keine Cookie-/Tracking-Hinweise (bei Web-Komponenten)
|
||||
- Keine Widerspruchsmöglichkeit
|
||||
- Keine Auskunftsfunktion (DSG Art. 25)
|
||||
|
||||
#### Blocker für externes Audit:
|
||||
- Ja. Fundamentale rechtliche Anforderung nicht erfüllt.
|
||||
|
||||
#### Empfohlene nächste Schritte:
|
||||
1. Privacy Policy erstellen (Schweizer Recht, medizinischer Kontext)
|
||||
2. Einwilligungsdialog für KI-Verarbeitung in die App integrieren
|
||||
3. Auskunftsprozess definieren (Patientenrecht auf Einsicht)
|
||||
|
||||
---
|
||||
|
||||
## GESAMTBEWERTUNG
|
||||
|
||||
### Ampelübersicht
|
||||
|
||||
| Bereich | Status | Audit-Blocker |
|
||||
|---------|:------:|:---:|
|
||||
| 1.1 Transport Security | YELLOW | Nein |
|
||||
| 1.2 Authentifizierung | YELLOW | Teilweise |
|
||||
| 1.3 Secrets Management | GREEN | Nein |
|
||||
| 1.4 2FA | YELLOW | Teilweise |
|
||||
| 1.5 Logging & Monitoring | RED | Ja |
|
||||
| 2.1 TOMs-Dokumentation | GREEN | Nein |
|
||||
| 2.2 Incident Management | YELLOW | Teilweise |
|
||||
| 2.3 Datenminimierung | RED | Ja |
|
||||
| 2.4 Löschkonzepte | RED | Ja |
|
||||
| 3.1 Rollen | YELLOW | Teilweise |
|
||||
| 3.2 Schulung | RED | Ja |
|
||||
| 3.3 Prozesse | RED | Teilweise |
|
||||
| 4.1 Backup & Recovery | RED | Ja |
|
||||
| 4.2 Patching | RED | Teilweise |
|
||||
| 4.3 Monitoring | RED | Teilweise |
|
||||
| 5.1 Informationspflichten | RED | Ja |
|
||||
| 5.2 AV-Verträge | RED | Ja |
|
||||
| 5.3 Privacy Policy | RED | Ja |
|
||||
|
||||
### Zählung
|
||||
|
||||
| Status | Anzahl |
|
||||
|--------|:------:|
|
||||
| GREEN | 2 |
|
||||
| YELLOW | 5 |
|
||||
| RED | 11 |
|
||||
|
||||
---
|
||||
|
||||
### Gesamturteil
|
||||
|
||||
#### DSG-konform: NEIN
|
||||
|
||||
Hauptgründe: Fehlende Informationspflichten (Art. 19–21), fehlende
|
||||
AV-Verträge (Art. 9), fehlendes Löschkonzept (Art. 32), fehlende
|
||||
Nachvollziehbarkeit (Art. 8).
|
||||
|
||||
#### DSGVO-konform: NEIN
|
||||
|
||||
Hauptgründe: Fehlende Privacy Policy, fehlende Einwilligungen für
|
||||
KI-Verarbeitung, fehlende DPAs, fehlende Datenschutz-Folgenabschätzung
|
||||
(DSFA/DPIA) für Gesundheitsdatenverarbeitung.
|
||||
|
||||
#### Medizinisch auditfähig: NEIN
|
||||
|
||||
Hauptgründe: Kein Backup-Konzept, kein Logging, keine Schulung,
|
||||
keine Informationspflichten gegenüber Patienten.
|
||||
|
||||
#### HIN-nah: TEILWEISE
|
||||
|
||||
Technische Basis (TLS, Hashing, 2FA) ist vorhanden.
|
||||
Netzwerk-/PKI-/S/MIME-Ebene fehlt komplett.
|
||||
Organisatorische Massnahmen fehlen weitgehend.
|
||||
|
||||
---
|
||||
|
||||
### Priorisierte Top-10-Massnahmen für Audit-Readiness
|
||||
|
||||
| Prio | Massnahme | Bereich | Aufwand |
|
||||
|:----:|-----------|---------|:-------:|
|
||||
| 1 | Datenschutzerklärung + Einwilligung KI | Recht | Niedrig |
|
||||
| 2 | OpenAI/Supabase DPA prüfen/abschliessen | Recht | Niedrig |
|
||||
| 3 | Incident-Passwort-Rotation abschliessen | Incident | Niedrig |
|
||||
| 4 | Automatisiertes Backup einrichten | Betrieb | Mittel |
|
||||
| 5 | Audit-Logging in API-Endpunkte integrieren | Technik | Mittel |
|
||||
| 6 | Datenverarbeitungsverzeichnis erstellen | Datenschutz | Mittel |
|
||||
| 7 | Löschkonzept + Aufbewahrungsfristen definieren | Datenschutz | Mittel |
|
||||
| 8 | Kurzschulung Datenschutz erstellen | Organisation | Niedrig |
|
||||
| 9 | Minimale Passwortlänge auf 8+ erhöhen | Technik | Niedrig |
|
||||
| 10 | AZA_2FA_REQUIRED=1 als Produktiv-Standard | Technik | Niedrig |
|
||||
|
||||
---
|
||||
|
||||
*Erstellt: 2026-02-22*
|
||||
*Nächste Überprüfung: Nach Umsetzung der priorisierten Massnahmen*
|
||||
*Klassifikation: Intern – Nicht zur externen Weitergabe*
|
||||
304
backup 24.2.26 - Kopie/security/compliance/TOMs_AZA_MedWork.md
Normal file
304
backup 24.2.26 - Kopie/security/compliance/TOMs_AZA_MedWork.md
Normal file
@@ -0,0 +1,304 @@
|
||||
# Technische und Organisatorische Massnahmen (TOMs)
|
||||
# AZA / MedWork – Medizinische Desktop- und Backend-Anwendung
|
||||
|
||||
**Version:** 1.0
|
||||
**Stand:** 2026-02-22
|
||||
**Geltungsbereich:** AZA/MedWork Gesamtsystem (Desktop-Client, Backend-Services, E-Mail-Modul, Workforce Planner)
|
||||
**Rechtsgrundlage:** Schweizer Datenschutzgesetz (DSG, rev. 2023), DSGVO (ergänzend)
|
||||
**Datenklassifikation:** Besonders schützenswerte Personendaten (Gesundheitsdaten, Art. 5 lit. c DSG)
|
||||
|
||||
---
|
||||
|
||||
## 1. Zugangskontrolle
|
||||
|
||||
*Massnahmen, die verhindern, dass Unbefugte Zugang zu Datenverarbeitungsanlagen erhalten.*
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Passwort-geschützter Login | Desktop-Client (basis14.py): Passwort-Dialog bei jedem Start, kein Zugriff ohne Authentifizierung | basis14.py, Zeilen 1318–1366 |
|
||||
| Workforce Planner: Token-basierte Authentifizierung | JWT (HS256) mit Ablaufzeit 480 Minuten, Bearer-Token in HTTP-Header | workforce_planner/api/auth.py |
|
||||
| Backend API-Token | API-Zugriff auf Backend-Services nur mit gültigem Token (Header X-Api-Token) | backend_main.py |
|
||||
| TLS-Verschlüsselung aller Backend-Services | HTTPS erzwungen für todo_server, transcribe_server, backend_main; HTTP wird abgelehnt (kein Redirect, Connection Refused) | aza_tls.py, STEP 4.1 |
|
||||
| Fail-Start bei fehlender TLS-Konfiguration | Server startet nicht, wenn AZA_TLS_REQUIRE=1 und Zertifikate fehlen | aza_tls.py |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Keine Multi-Faktor-Authentifizierung (2FA/MFA)
|
||||
- Keine biometrische Zugangskontrolle
|
||||
- Keine automatische Bildschirmsperre der Anwendung bei Inaktivität
|
||||
- Kein SSO-Protokoll (SAML/OAuth/OIDC)
|
||||
- Keine zertifikatsbasierte Client-Authentifizierung
|
||||
|
||||
---
|
||||
|
||||
## 2. Zugriffskontrolle
|
||||
|
||||
*Massnahmen, die sicherstellen, dass Berechtigte nur auf die ihnen zugewiesenen Daten zugreifen können.*
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Rollenbasierte Zugriffskontrolle (RBAC) | Workforce Planner: 3 Rollen (ADMIN, MANAGER, EMPLOYEE) mit Dependency-basierter Prüfung pro API-Endpunkt | workforce_planner/core/enums.py, api/auth.py |
|
||||
| Rollenprüfung bei schreibenden Operationen | Mitarbeiterverwaltung und Abwesenheitsmanagement erfordern ADMIN oder MANAGER Rolle | routes_employees.py, routes_absences.py |
|
||||
| Löschoperationen nur für ADMIN | Mitarbeiter-Löschung ist auf ADMIN-Rolle beschränkt | routes_employees.py |
|
||||
| Profil-Zugriff mit Passwortprüfung | Passwortänderung im Desktop-Client erfordert Eingabe des alten Passworts | basis14.py, Zeilen 1545–1554 |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Keine granulare Datenzugriffskontrolle auf Patienten-/Datensatzebene
|
||||
- Kein Berechtigungskonzept für Patientenakten (KG)
|
||||
- Keine Zugriffsmatrix dokumentiert
|
||||
- Keine regelmässige Überprüfung der Berechtigungen
|
||||
|
||||
---
|
||||
|
||||
## 3. Weitergabekontrolle
|
||||
|
||||
*Massnahmen, die verhindern, dass personenbezogene Daten bei der Übermittlung unbefugt gelesen oder verändert werden.*
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| TLS >= 1.2 für alle Backend-Services | Erzwungen via zentrale Konfiguration; TLS 1.3 wird bevorzugt; starke Cipher Suites (ECDHE+AESGCM, ECDHE+CHACHA20, DHE+AESGCM) | aza_tls.py, STEP 4.1 |
|
||||
| Perfect Forward Secrecy (PFS) | Cipher Suites erfordern ECDHE oder DHE Schlüsselaustausch | aza_tls.py |
|
||||
| IMAP über SSL/TLS | E-Mail-Empfang über IMAP4_SSL (Port 993) | aza_email.py |
|
||||
| SMTP mit STARTTLS | E-Mail-Versand über SMTP mit STARTTLS (Port 587) | aza_email.py |
|
||||
| Credentials nicht in Dateien | E-Mail-Passwörter werden ausschliesslich aus ENV-Variablen geladen, nie auf Disk geschrieben | aza_email.py, STEP 4.4 |
|
||||
| Secret Keys nicht hardcoded | JWT-Signing-Key kommt aus ENV (AZA_SECRET_KEY), mit Validierung auf Länge und Komplexität | workforce_planner/config.py, STEP 4.3 |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Keine Ende-zu-Ende-Verschlüsselung für E-Mails (kein S/MIME, kein PGP)
|
||||
- Keine Signierung von E-Mails
|
||||
- Kein VPN oder Netzwerk-Tunnel
|
||||
- Keine Zertifikatsverifikation im SMTP-Client (STARTTLS ist opportunistisch)
|
||||
- Keine Verschlüsselung lokaler Daten auf Disk (SQLite-Datenbank, JSON-Dateien)
|
||||
|
||||
---
|
||||
|
||||
## 4. Eingabekontrolle
|
||||
|
||||
*Massnahmen, die nachvollziehbar machen, wer wann welche Daten eingegeben oder verändert hat.*
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Audit-Log für schreibende Aktionen | Workforce Planner: AuditLog-Tabelle mit user_id, action, entity_type, entity_id, old_values, new_values, timestamp, ip_address, user_agent | workforce_planner/core/models.py, api/audit.py |
|
||||
| Benutzeridentifikation | Jede API-Aktion wird dem authentifizierten Benutzer zugeordnet (JWT-Token enthält employee_id und role) | workforce_planner/api/auth.py |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Audit-Log-Funktion ist definiert, aber nicht in alle API-Endpunkte integriert (log_action wird nur in audit.py referenziert, keine Aufrufe in Routes gefunden)
|
||||
- Kein Audit-Log für Desktop-Client-Aktionen (basis14.py)
|
||||
- Kein Audit-Log für E-Mail-Aktionen
|
||||
- Keine Protokollierung von Datenzugriffen (nur schreibende Aktionen vorgesehen)
|
||||
- Kein manipulationssicheres Logging (kein WORM, kein Syslog)
|
||||
|
||||
---
|
||||
|
||||
## 5. Auftragskontrolle
|
||||
|
||||
*Massnahmen, die sicherstellen, dass im Auftrag verarbeitete Daten nur gemäss Weisung verarbeitet werden.*
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Externe Dienstleister dokumentiert | OpenAI API für Transkription und KI-Funktionen; Supabase für Cloud-Speicher | basis14.py, aza_config.py |
|
||||
| API-Key-basierter Zugriff auf externe Dienste | OpenAI-Zugriff nur mit gültigem API-Key aus ENV-Variable | basis14.py |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Kein formaler Auftragsverarbeitungsvertrag (AVV/DPA) mit OpenAI dokumentiert
|
||||
- Kein formaler AVV/DPA mit Supabase dokumentiert
|
||||
- Keine Prüfung der Datenverarbeitungsstandorte der Unterauftragnehmer
|
||||
- Keine vertragliche Regelung zur Löschung nach Vertragsende
|
||||
- Keine Prüfung der technischen Massnahmen der Unterauftragnehmer
|
||||
|
||||
---
|
||||
|
||||
## 6. Verfügbarkeitskontrolle
|
||||
|
||||
*Massnahmen zum Schutz personenbezogener Daten gegen Zerstörung oder Verlust.*
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Lokale Datenspeicherung | Patientendaten, Konfigurationen und Notizen werden lokal als JSON/SQLite gespeichert | aza_persistence.py |
|
||||
| Manuelle Backup-Kopien | Projektverzeichnis enthält mehrere Backup-Kopien (basis14 - Kopie*.py) | Dateisystem |
|
||||
| Fail-Start-Mechanismen | Server starten nicht bei fehlender Konfiguration (TLS, Secret Key) – verhindert ungesicherten Betrieb | aza_tls.py, workforce_planner/config.py |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Kein automatisiertes Backup-Konzept
|
||||
- Kein Disaster-Recovery-Plan
|
||||
- Keine Redundanz der Backend-Services
|
||||
- Kein Monitoring der Dienstverfügbarkeit
|
||||
- Kein USV-Schutz dokumentiert
|
||||
- Keine regelmässigen Wiederherstellungstests
|
||||
|
||||
---
|
||||
|
||||
## 7. Trennungsgebot
|
||||
|
||||
*Massnahmen, die sicherstellen, dass zu unterschiedlichen Zwecken erhobene Daten getrennt verarbeitet werden.*
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Modulare Architektur | Separate Module für KG-Diktat (basis14), E-Mail (aza_email), Workforce Planning (workforce_planner), Todo (todo_server), Transkription (transcribe_server) | Dateisystem |
|
||||
| Getrennte Datenbanken | Workforce Planner: eigene SQLite-Datenbank (workforce_planner.db); Desktop-Client: separate JSON-Config-Dateien | workforce_planner/config.py, aza_persistence.py |
|
||||
| Getrennte Konfigurationen | Jedes Modul hat eigene Konfigurationsdateien | aza_config.py |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Keine mandantenfähige Datentrennung
|
||||
- Keine formale Datenklassifikation implementiert
|
||||
- Keine technische Trennung von Produktiv-/Testdaten
|
||||
- Keine Trennung von medizinischen und administrativen Daten auf Datenbankebene
|
||||
|
||||
---
|
||||
|
||||
## 8. Incident Management
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Incident-Dokumentation | Formale Incident-Notiz erstellt bei Entdeckung exponierter Credentials | INCIDENT_2026-02-22_CREDENTIAL_EXPOSURE.md |
|
||||
| Sofortmassnahmen dokumentiert | Checkliste mit Verantwortlichkeiten und Status | INCIDENT_2026-02-22_CREDENTIAL_EXPOSURE.md |
|
||||
| Security-Handover-Dokumentation | Jeder Sicherheitsschritt wird formal dokumentiert mit Änderungen, Tests und Restrisiken | /security/handovers/ |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Kein formaler Incident-Response-Plan
|
||||
- Keine definierten Eskalationsstufen
|
||||
- Keine Meldepflicht-Prozesse (DSG Art. 24: Meldung an EDÖB innert 72h)
|
||||
- Keine automatische Anomalie-Erkennung
|
||||
- Kein Security Operations Center (SOC)
|
||||
|
||||
---
|
||||
|
||||
## 9. Backup & Recovery
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Manuelle Dateisystem-Backups | Mehrere datierte Backup-Kopien des Projekts vorhanden | Dateisystem (backup-Ordner) |
|
||||
| Lokale Datenpersistenz | Alle Daten lokal gespeichert (kein Single Point of Failure durch Cloud-Abhängigkeit) | aza_persistence.py |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Kein automatisiertes, regelmässiges Backup
|
||||
- Keine Versionierung der Datenbanken
|
||||
- Kein Off-Site-Backup
|
||||
- Keine dokumentierte Recovery-Prozedur
|
||||
- Keine Recovery Time Objective (RTO) / Recovery Point Objective (RPO) definiert
|
||||
- Keine regelmässigen Wiederherstellungstests
|
||||
|
||||
---
|
||||
|
||||
## 10. Patch- & Update-Management
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Python-Paketmanagement | Dependencies über pip/requirements installierbar | Projektstruktur |
|
||||
| Aktuelle Kryptographie-Bibliotheken | bcrypt und cryptography-Bibliothek im Einsatz | basis14.py, aza_tls.py |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Kein dokumentiertes Patch-Management-Verfahren
|
||||
- Keine automatische Dependency-Prüfung auf bekannte Schwachstellen (kein pip-audit, kein Dependabot)
|
||||
- Keine regelmässigen Update-Zyklen definiert
|
||||
- Keine Testprozedur vor Produktiv-Updates
|
||||
- Kein Changelog oder Release-Management
|
||||
|
||||
---
|
||||
|
||||
## 11. Logging & Monitoring
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Audit-Log-Modell | Datenbankmodell für Änderungsprotokoll mit Benutzer, Aktion, Zeitstempel, alte/neue Werte, IP-Adresse | workforce_planner/core/models.py (AuditLog) |
|
||||
| Debug-Logging | Print-basiertes Debug-Logging in E-Mail-Modul (SMTP/IMAP-Verbindungen) | aza_email.py |
|
||||
| Sicherheitswarnungen auf stderr | Migrationswarnung bei Klartext-Credentials, Fail-Start-Meldungen bei fehlender Konfiguration | aza_email.py, aza_tls.py, workforce_planner/config.py |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Audit-Log-Integration in API-Endpunkte nicht vollständig
|
||||
- Kein zentrales Logging-Framework (kein strukturiertes Logging)
|
||||
- Kein Log-Rotation-Mechanismus
|
||||
- Kein Monitoring-Dashboard
|
||||
- Keine Alerting-Mechanismen
|
||||
- Keine Protokollierung fehlgeschlagener Anmeldeversuche
|
||||
- Kein SIEM (Security Information and Event Management)
|
||||
|
||||
---
|
||||
|
||||
## 12. Mitarbeiterschulung
|
||||
|
||||
### Implementiert
|
||||
|
||||
| Massnahme | Umsetzung | Referenz |
|
||||
|-----------|-----------|----------|
|
||||
| Keine | Keine formalen Schulungsmassnahmen implementiert | — |
|
||||
|
||||
### Nicht implementiert
|
||||
|
||||
- Keine Security-Awareness-Schulungen
|
||||
- Keine Dokumentation für Endbenutzer zum sicheren Umgang
|
||||
- Keine Schulung zum Umgang mit Patientendaten
|
||||
- Keine Phishing-Awareness
|
||||
- Keine dokumentierte Einweisungsprozedur für neue Benutzer
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Bereich | Implementiert | Teilweise | Nicht implementiert |
|
||||
|---------|:---:|:---:|:---:|
|
||||
| 1. Zugangskontrolle | 5 | — | 5 |
|
||||
| 2. Zugriffskontrolle | 4 | — | 4 |
|
||||
| 3. Weitergabekontrolle | 6 | — | 5 |
|
||||
| 4. Eingabekontrolle | 2 | — | 5 |
|
||||
| 5. Auftragskontrolle | 2 | — | 5 |
|
||||
| 6. Verfügbarkeitskontrolle | 3 | — | 6 |
|
||||
| 7. Trennungsgebot | 3 | — | 4 |
|
||||
| 8. Incident Management | 3 | — | 5 |
|
||||
| 9. Backup & Recovery | 2 | — | 6 |
|
||||
| 10. Patch- & Update-Management | 2 | — | 5 |
|
||||
| 11. Logging & Monitoring | 3 | — | 7 |
|
||||
| 12. Mitarbeiterschulung | 0 | — | 5 |
|
||||
|
||||
**Total: 35 Massnahmen implementiert, 62 Massnahmen offen**
|
||||
|
||||
### Implementierte Security-Schritte (Bezug)
|
||||
|
||||
| Schritt | Massnahme | Status |
|
||||
|---------|-----------|--------|
|
||||
| STEP 4.1 | TLS-Pflicht für alle Backend-Services | Erledigt, verifiziert (STEP 4.1a) |
|
||||
| STEP 4.2 | Sicheres Passwort-Hashing (bcrypt, cost=12) | Erledigt, verifiziert (STEP 4.2a) |
|
||||
| STEP 4.3 | Hardcoded Secret Key entfernt | Erledigt, getestet |
|
||||
| STEP 4.4 | Klartext-Credentials entfernt | Erledigt, getestet |
|
||||
| STEP 4.4a | Incident-Dokumentation Credential Exposure | Erledigt |
|
||||
|
||||
---
|
||||
|
||||
*Dieses Dokument beschreibt den Ist-Zustand der technischen und organisatorischen
|
||||
Massnahmen des AZA/MedWork-Systems. Es dient als Grundlage für die Weiterentwicklung
|
||||
des Sicherheitskonzepts und als Nachweis gegenüber Aufsichtsbehörden.*
|
||||
|
||||
*Nächste Überprüfung: Bei Abschluss weiterer Security-Schritte oder spätestens
|
||||
nach 6 Monaten.*
|
||||
@@ -0,0 +1,136 @@
|
||||
# INCIDENT REPORT – CREDENTIAL EXPOSURE
|
||||
# Datum: 2026-02-22
|
||||
# Klassifikation: SICHERHEITSVORFALL (intern)
|
||||
|
||||
---
|
||||
|
||||
## 1. Zusammenfassung
|
||||
|
||||
Im Rahmen der systematischen Sicherheitsanalyse (STEP 4.4 – Klartext-Credentials
|
||||
entfernen) wurde ein produktives E-Mail-Passwort im Klartext in einer
|
||||
Konfigurationsdatei des Projekts entdeckt.
|
||||
|
||||
---
|
||||
|
||||
## 2. Betroffene Komponente
|
||||
|
||||
- **Datei:** `aza_email_config.json`
|
||||
- **Modul:** AZA E-Mail (aza_email.py)
|
||||
- **Feld:** `password` im `accounts`-Array
|
||||
- **Betroffener Account:** Produktiver Mail-Account (Details bewusst nicht aufgeführt)
|
||||
|
||||
---
|
||||
|
||||
## 3. Entdeckung
|
||||
|
||||
- **Entdeckt durch:** Automatisierte Security Gap-Analyse (STEP 3) und
|
||||
bestätigt bei der Implementierung von STEP 4.4
|
||||
- **Entdeckungsdatum:** 2026-02-22
|
||||
- **Entdeckungsmethode:** Grep-Suche nach `password`, `secret`, `token`
|
||||
in allen JSON/YAML/Config-Dateien des Projekts
|
||||
|
||||
---
|
||||
|
||||
## 4. Risikoanalyse
|
||||
|
||||
### Worst-Case-Szenarien
|
||||
|
||||
| Szenario | Risiko | Schwere |
|
||||
|----------|--------|---------|
|
||||
| Unbefugter Zugriff auf das Dateisystem | Passwort-Exfiltration | HOCH |
|
||||
| Account-Übernahme (Mail) | Lesen/Senden von E-Mails im Namen des Kontoinhabers | HOCH |
|
||||
| Mail-Missbrauch | Spam, Phishing, Social Engineering über legitimen Account | HOCH |
|
||||
| Laterale Bewegung | Falls Passwort wiederverwendet: Zugriff auf weitere Systeme | KRITISCH |
|
||||
| Datenschutzverletzung | Zugriff auf medizinische Kommunikation (Patientendaten) | KRITISCH |
|
||||
| Backup-/Export-Exposition | Passwort in Backups, Cloud-Syncs oder Transfers vorhanden | MITTEL |
|
||||
|
||||
### Besondere Faktoren
|
||||
|
||||
- **Medizinischer Kontext:** E-Mail-Account einer Arztpraxis – potenziell
|
||||
Patientendaten in Mails (DSG/DSGVO-relevant)
|
||||
- **Expositionsdauer:** Unbekannt. Das Passwort war seit der Erstellung
|
||||
der Konfigurationsdatei im Klartext gespeichert.
|
||||
- **Zugriffskreis:** Alle Personen/Systeme mit Lesezugriff auf das
|
||||
Projektverzeichnis, Backups oder Transfers dieser Datei.
|
||||
|
||||
---
|
||||
|
||||
## 5. Sofortmassnahmen
|
||||
|
||||
| Nr | Massnahme | Status | Verantwortlich |
|
||||
|----|-----------|--------|----------------|
|
||||
| 1 | Passwort aus Konfigurationsdatei entfernt | ERLEDIGT (STEP 4.4) | Security Engineer |
|
||||
| 2 | Code umgestellt auf ENV-basierte Credentials | ERLEDIGT (STEP 4.4) | Security Engineer |
|
||||
| 3 | Migrationswarnung implementiert | ERLEDIGT (STEP 4.4) | Security Engineer |
|
||||
| 4 | **Passwort beim Mail-Provider rotieren** | **OFFEN – DRINGEND** | Projektinhaber |
|
||||
| 5 | Prüfung ob Account kompromittiert wurde | OFFEN | Projektinhaber |
|
||||
|
||||
---
|
||||
|
||||
## 6. Empfohlene Folgeaktionen
|
||||
|
||||
### Sofort (innerhalb 24h)
|
||||
|
||||
- [ ] **Passwort beim Mail-Provider ändern** (neues, starkes Passwort,
|
||||
mindestens 16 Zeichen, keine Wiederverwendung)
|
||||
- [ ] **2FA aktivieren**, falls der Mail-Provider dies unterstützt
|
||||
- [ ] **Login-Protokolle prüfen**: Gibt es unbekannte Zugriffe auf den
|
||||
betroffenen Mail-Account?
|
||||
|
||||
### Kurzfristig (innerhalb 1 Woche)
|
||||
|
||||
- [ ] **Passwort-Wiederverwendung prüfen**: Wurde dasselbe Passwort für
|
||||
andere Dienste/Systeme verwendet? Falls ja: dort ebenfalls rotieren.
|
||||
- [ ] **Backups durchsuchen**: Prüfen, ob Kopien der Datei
|
||||
`aza_email_config.json` in Backups, Cloud-Speicher, USB-Sticks,
|
||||
E-Mail-Anhängen oder anderen Transfers vorhanden sind.
|
||||
Falls ja: Passwort dort ebenfalls als exponiert betrachten.
|
||||
- [ ] **Projekt-Kopien prüfen**: Alle `backup *`-Ordner und Kopien
|
||||
des Projektverzeichnisses auf Klartext-Passwörter durchsuchen.
|
||||
|
||||
### Mittelfristig (Empfehlung)
|
||||
|
||||
- [ ] **Secret-Scanning** in CI/CD-Pipeline einführen (z.B. git-secrets,
|
||||
truffleHog, GitHub Secret Scanning), um zukünftige Klartext-Credentials
|
||||
automatisch zu erkennen.
|
||||
- [ ] **OS-Keychain-Integration** evaluieren (Windows Credential Manager),
|
||||
um Passwörter sicher und benutzerfreundlich zu speichern.
|
||||
|
||||
---
|
||||
|
||||
## 7. Lessons Learned
|
||||
|
||||
### Ursache
|
||||
|
||||
Das Passwort wurde direkt im GUI-Dialog eingegeben und ohne
|
||||
Schutzmassnahmen in eine JSON-Datei auf der Festplatte geschrieben.
|
||||
Es gab keine Trennung zwischen Konfiguration und Credentials.
|
||||
|
||||
### Präventionsmassnahmen (implementiert)
|
||||
|
||||
1. **Credentials werden NIE mehr in Dateien geschrieben**
|
||||
(`_strip_passwords()` in `save_email_config()`)
|
||||
2. **Credentials kommen ausschliesslich aus ENV-Variablen**
|
||||
(`AZA_EMAIL_PASSWORD_0`, `_1`, ...)
|
||||
3. **Migrationswarnung** bei bestehenden Klartext-Passwörtern in der Config
|
||||
4. **Klartext-Passwörter in JSON werden ignoriert** (kein Auto-Migrate)
|
||||
|
||||
### Präventionsmassnahmen (empfohlen, nicht implementiert)
|
||||
|
||||
1. Secret-Scanning in CI/CD
|
||||
2. Pre-Commit-Hooks für Secret-Detection
|
||||
3. OS-Keychain als sicherer Credential-Store
|
||||
4. Regelmässige Audits der Konfigurationsdateien
|
||||
|
||||
---
|
||||
|
||||
## 8. Referenzen
|
||||
|
||||
- STEP 3 – GAP-Analyse: Lücke #28 identifiziert
|
||||
- STEP 4.4 – Implementierung der Credential-Entfernung
|
||||
- STEP 4.4 Handover: `/security/handovers/STEP_4_4_CREDENTIALS.md`
|
||||
|
||||
---
|
||||
|
||||
*Erstellt: 2026-02-22 im Rahmen des AZA/MedWork Security & Compliance Projects*
|
||||
*Klassifikation: Intern – Nicht zur externen Weitergabe bestimmt*
|
||||
32
backup 24.2.26 - Kopie/security/handovers/STEP_01_SUMMARY.md
Normal file
32
backup 24.2.26 - Kopie/security/handovers/STEP_01_SUMMARY.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# STEP 1 – HIN TECHNICAL REFERENCE COLLECTION
|
||||
# Status: ABGESCHLOSSEN
|
||||
|
||||
---
|
||||
|
||||
## Was gemacht wurde
|
||||
|
||||
- Ordner /security/reference/hin_docs/ erstellt
|
||||
- Öffentlich verfügbare technische Dokumentation von HIN gesammelt
|
||||
- 16 Originalquellen (hin.ch, support.hin.ch, cdn.hin.ch, msxfaq.de) ausgewertet
|
||||
- Technische Referenzdatei erstellt: hin_architektur.md
|
||||
- Quellenverzeichnis erstellt: hin_quellen.md
|
||||
- Support-Anfrage vorbereitet: QUELLEN_UND_ANLEITUNG.md
|
||||
|
||||
## Was geändert wurde
|
||||
|
||||
- NEU: /security/reference/hin_docs/hin_architektur.md
|
||||
- NEU: /security/reference/hin_docs/hin_quellen.md
|
||||
- NEU: /security/reference/hin_docs/QUELLEN_UND_ANLEITUNG.md
|
||||
|
||||
Keine Codeänderungen. Keine Architekturänderungen.
|
||||
|
||||
## Offene Punkte
|
||||
|
||||
- Offizielle HIN-PDFs sind nicht frei downloadbar
|
||||
- Support-Anfrage an support@hin.ch ist vorbereitet aber NICHT gesendet (muss User tun)
|
||||
- HIN Gateway Dokumentation (ZIP) unter https://download.hin.ch/gw/hin-gateway.zip nicht heruntergeladen
|
||||
|
||||
## Risiken
|
||||
|
||||
- Alle Daten stammen aus öffentlichen Quellen, nicht aus offizieller HIN-Vertragsdokumentation
|
||||
- Einige technische Details (TLS-Versionen, Cipher Suites) sind in öffentlichen Quellen nicht verfügbar
|
||||
70
backup 24.2.26 - Kopie/security/handovers/STEP_02_SUMMARY.md
Normal file
70
backup 24.2.26 - Kopie/security/handovers/STEP_02_SUMMARY.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# STEP 2 – TECHNISCHE ZERLEGUNG VON HIN
|
||||
# Status: ABGESCHLOSSEN
|
||||
|
||||
---
|
||||
|
||||
## Was gemacht wurde
|
||||
|
||||
Analyse der HIN-Dokumente aus /security/reference/hin_docs/.
|
||||
Strukturierte Extraktion des HIN Security Stacks in 5 Bereiche.
|
||||
|
||||
## Ergebnis (Kurzfassung)
|
||||
|
||||
### 1. Transport
|
||||
- TLS: optional, nicht erzwungen
|
||||
- S/MIME ist primärer Transportschutz (Legacy)
|
||||
- Wireguard für Stargate (neue Generation)
|
||||
- SCION für SSHN-Netzwerk
|
||||
- TLS-Version, Cipher Suites, PFS: NICHT BELEGT
|
||||
|
||||
### 2. Netzwerk
|
||||
- Geschlossener Vertrauensraum (Gated Community)
|
||||
- SCION/SSHN mit Schweizer-only Routing
|
||||
- DDoS-Schutz durch Architektur
|
||||
- 1 Instanz pro Organisation (Segmentierung)
|
||||
- Zero Trust, IP-Whitelisting: NICHT BELEGT
|
||||
|
||||
### 3. PKI
|
||||
- Eigene CA: "HIN MGW Issuing CA 2022"
|
||||
- RSA 4096-bit, SHA256withRSA
|
||||
- Domain-Zertifikate (10 Jahre Gültigkeit)
|
||||
- SSHN-Zertifikate (72h Gültigkeit)
|
||||
- Zentrale Verteilung, geschlossenes System
|
||||
- Hardware-Token, Smartcard: NICHT BELEGT
|
||||
|
||||
### 4. Authentifizierung
|
||||
- 2FA in drei Varianten (Client, Gateway, Mobil)
|
||||
- Identitätsprüfung via Videoidentifikation
|
||||
- SSO für HIN-geschützte Anwendungen
|
||||
- eID-Typen: Persönlich, Organisation, Team
|
||||
- SAML/OAuth/OIDC: NICHT BELEGT
|
||||
|
||||
### 5. Mail
|
||||
- S/MIME Gateway-zu-Gateway (nicht Ende-zu-Ende)
|
||||
- Domain-Zertifikate, automatische Verschlüsselung
|
||||
- HIN Mail Global: GINA/SEPPMail, SMS-Auth, 30 Tage
|
||||
- Betreffzeile bei HIN Mail Global: UNVERSCHLÜSSELT
|
||||
- Backend: SEPPMail-Appliance, Zimbra (IMAP)
|
||||
|
||||
## Was geändert wurde
|
||||
|
||||
Keine Dateien geändert. Analyse wurde im Chat ausgegeben.
|
||||
|
||||
## Offene Punkte
|
||||
|
||||
1. TLS-Version, Cipher Suites, PFS – nicht dokumentiert
|
||||
2. mTLS – unklar
|
||||
3. SSO-Protokoll (SAML/OAuth/OIDC) – nicht dokumentiert
|
||||
4. Hardware-Token / Smartcard – nicht dokumentiert
|
||||
5. Client-Zertifikate vs. Software-Token – nicht spezifiziert
|
||||
6. Zero Trust Modell – nicht explizit
|
||||
7. Post-Quanten-Kryptografie – nur "vorbereitet", keine Details
|
||||
8. Wireguard-Konfiguration – keine Details
|
||||
9. Ende-zu-Ende Option – nicht dokumentiert
|
||||
10. IP-Whitelisting – nicht dokumentiert
|
||||
|
||||
## Risiken
|
||||
|
||||
- 10 von 20+ technischen Parametern sind in öffentlichen Quellen NICHT BELEGT
|
||||
- Gap-Analyse wird in diesen Bereichen auf konservative Annahmen angewiesen sein
|
||||
- Ohne offizielle HIN-Dokumentation bleiben diese Lücken bestehen
|
||||
209
backup 24.2.26 - Kopie/security/handovers/STEP_03_GAP_ANALYSE.md
Normal file
209
backup 24.2.26 - Kopie/security/handovers/STEP_03_GAP_ANALYSE.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# =====================================================================
|
||||
# STEP 3 – FORMELLE GAP-ANALYSE (HIN vs. AZA)
|
||||
# =====================================================================
|
||||
# Datenquellen: /security/reference/hin_docs/ + Projekt-Code
|
||||
# Methode: Nur technisch belegbare Fakten
|
||||
# =====================================================================
|
||||
|
||||
## 1. Transport Security
|
||||
|
||||
### HIN:
|
||||
- S/MIME als primärer Transportschutz (nicht TLS)
|
||||
- TLS optional, nicht erzwungen auf SMTP-Ebene
|
||||
- Wireguard-Protokoll für Stargate (App-zu-App, kein VPN)
|
||||
- SCION-Netzwerk für SSHN (any-to-any Verschlüsselung)
|
||||
- Routing ausschliesslich über Schweizer Netze
|
||||
|
||||
### AZA:
|
||||
- SMTP mit STARTTLS (Port 587) – aza_email.py Zeilen 1433–1436, 2994–2997
|
||||
- IMAP über SSL/TLS (Port 993, IMAP4_SSL) – aza_email.py Zeilen 1470, 1705
|
||||
- Keine eigene TLS-Konfiguration (Python-Defaults)
|
||||
- Keine Zertifikatsverifikation im Code gesetzt
|
||||
- todo_server.py: reiner HTTP-Server, kein HTTPS – Zeile 228
|
||||
- transcribe_server.py: kein TLS
|
||||
- backend_main.py: kein TLS
|
||||
- Supabase-Zugriff über HTTPS – aza_config.py Zeile 176
|
||||
|
||||
### GAP:
|
||||
| Aspekt | HIN | AZA | Lücke |
|
||||
|--------|-----|-----|-------|
|
||||
| Mail-Verschlüsselung | S/MIME (RSA 4096) | Nur STARTTLS (opportunistisch) | JA |
|
||||
| App-zu-App Transport | Wireguard | Kein verschlüsselter Kanal | JA |
|
||||
| Netzwerk-Routing | SCION, nur CH | Internet, global | JA |
|
||||
| Server-Kommunikation | Verschlüsselt | HTTP (Klartext) | JA |
|
||||
| TLS-Konfiguration | UNBEKANNT | Python-Defaults | UNBEKANNT |
|
||||
|
||||
### Kritikalität: **HIGH**
|
||||
|
||||
---
|
||||
|
||||
## 2. Netzwerkmodell
|
||||
|
||||
### HIN:
|
||||
- Geschlossener Vertrauensraum (Gated Community)
|
||||
- SCION/SSHN mit AS-Nummern und Zertifikaten
|
||||
- DDoS-Schutz durch Architektur
|
||||
- 1 Instanz pro Organisation (keine Mandantenfähigkeit)
|
||||
- Redundante Transportwege, automatischer Fail-over
|
||||
- Governance: HIN, FMH, pharmaSuisse, BAG, SWITCH
|
||||
|
||||
### AZA:
|
||||
- Kein Netzwerk-Perimeter definiert
|
||||
- Kein VPN
|
||||
- Kein DDoS-Schutz
|
||||
- Keine Netzwerk-Segmentierung
|
||||
- Einzige Netzwerk-Massnahme: Windows-Firewall-Regel für Todo-Server-Port – todo_server.py Zeilen 209–220
|
||||
- Backend-Services auf localhost (HTTP)
|
||||
- Supabase als externer Cloud-Service (HTTPS)
|
||||
|
||||
### GAP:
|
||||
| Aspekt | HIN | AZA | Lücke |
|
||||
|--------|-----|-----|-------|
|
||||
| Vertrauensraum | Geschlossen, verifiziert | Offen, kein Perimeter | JA |
|
||||
| DDoS-Schutz | SCION-basiert | Keiner | JA |
|
||||
| Segmentierung | 1 Instanz/Organisation | Keine | JA |
|
||||
| Redundanz | Multi-Path, Fail-over | Keine | JA |
|
||||
| Governance | 5 Organisationen | Keine | JA |
|
||||
| VPN | Wireguard (Stargate) | Keiner | JA |
|
||||
|
||||
### Kritikalität: **HIGH**
|
||||
|
||||
---
|
||||
|
||||
## 3. PKI
|
||||
|
||||
### HIN:
|
||||
- Eigene CA: "HIN MGW Issuing CA 2022"
|
||||
- RSA 4096-bit, SHA256withRSAEncryption
|
||||
- Domain-Zertifikate (10 Jahre Gültigkeit)
|
||||
- SSHN-Zertifikate (72h Gültigkeit, automatische Erneuerung)
|
||||
- Zentrale Zertifikatsverteilung über HIN-Backend
|
||||
- Geschlossenes System
|
||||
|
||||
### AZA:
|
||||
- Keine PKI
|
||||
- Keine eigene CA
|
||||
- Keine Zertifikate (weder Client- noch Server-Zertifikate)
|
||||
- Kein Zertifikatsmanagement
|
||||
- Kein CSR-Prozess
|
||||
- Keine Signierung von Daten oder Kommunikation
|
||||
|
||||
### GAP:
|
||||
| Aspekt | HIN | AZA | Lücke |
|
||||
|--------|-----|-----|-------|
|
||||
| Eigene CA | HIN MGW Issuing CA 2022 | Keine | JA |
|
||||
| Zertifikate | RSA 4096, Domain-Zerts | Keine | JA |
|
||||
| Zertifikatsverteilung | Zentral, automatisch | Nicht vorhanden | JA |
|
||||
| Schlüsselmanagement | Zentral über Backend | Nicht vorhanden | JA |
|
||||
| Hardware-Token | UNBEKANNT | Nicht vorhanden | UNBEKANNT |
|
||||
|
||||
### Kritikalität: **HIGH**
|
||||
|
||||
---
|
||||
|
||||
## 4. Authentifizierung
|
||||
|
||||
### HIN:
|
||||
- 2FA in drei Varianten:
|
||||
- HIN Client: Passwort + kryptographischer Geräteschlüssel
|
||||
- HIN Gateway: mTAN/Auth-App + Active Directory
|
||||
- Mobil: mTAN/Auth-App + HIN Login+Passwort
|
||||
- Identitätsprüfung via Videoidentifikation
|
||||
- eID-Typen: Persönlich, Organisation, Team
|
||||
- SSO für HIN-geschützte Anwendungen
|
||||
- Berufsqualifikation über Register/Verbände verifiziert
|
||||
|
||||
### AZA:
|
||||
- basis14.py: SHA-256 Passwort-Hash OHNE Salt – Zeilen 1298–1300
|
||||
- workforce_planner: bcrypt mit Salt – auth.py Zeilen 23–28
|
||||
- workforce_planner: JWT mit HS256 – auth.py Zeilen 31–37
|
||||
- workforce_planner: Secret Key hardcoded als Fallback "dev-secret-change-in-production" – config.py Zeile 19
|
||||
- workforce_planner: Token-Ablauf 480 Minuten (8h) – config.py Zeile 20
|
||||
- backend_main.py: API-Token via Header "X-Api-Token" – Zeilen 154–157
|
||||
- backend_main.py: User-Identifikation via Header "X-User" – Zeilen 162–164
|
||||
- Rollen: ADMIN, MANAGER, EMPLOYEE – enums.py Zeilen 33–36
|
||||
- Rollenbasierte Zugriffskontrolle mit require_role() – auth.py Zeilen 59–66
|
||||
- Keine 2FA / MFA / OTP
|
||||
- Keine zertifikatsbasierte Authentifizierung
|
||||
- Keine Identitätsprüfung (Videoidentifikation o.ä.)
|
||||
- Kein SSO-Protokoll (SAML/OAuth/OIDC)
|
||||
|
||||
### GAP:
|
||||
| Aspekt | HIN | AZA | Lücke |
|
||||
|--------|-----|-----|-------|
|
||||
| 2FA | Ja (3 Varianten) | Nein | JA |
|
||||
| Passwort-Hashing | UNBEKANNT | SHA-256 ohne Salt (basis14) / bcrypt (workforce) | JA (basis14) |
|
||||
| Secret Key | UNBEKANNT | Hardcoded Fallback | JA |
|
||||
| Identitätsprüfung | Videoidentifikation | Keine | JA |
|
||||
| SSO | Ja | Nein | JA |
|
||||
| Zertifikats-Auth | Ja (Geräteschlüssel) | Nein | JA |
|
||||
| Token-Ablauf | UNBEKANNT | 480 min (8h) | UNBEKANNT |
|
||||
| Rollen | eID-basiert (3 Typen) | RBAC (3 Rollen) | TEILWEISE |
|
||||
|
||||
### Kritikalität: **HIGH**
|
||||
|
||||
---
|
||||
|
||||
## 5. Mail-Sicherheit
|
||||
|
||||
### HIN:
|
||||
- S/MIME Gateway-zu-Gateway mit Domain-Zertifikaten (RSA 4096)
|
||||
- Automatische Verschlüsselung/Entschlüsselung durch Gateway
|
||||
- HIN Mail Global: GINA/SEPPMail für externe Empfänger
|
||||
- SMS-Code-Authentifizierung für externe Empfänger
|
||||
- Domain-Prüfung gegen HIN-Domainliste
|
||||
- Betreffzeile bei HIN Mail Global: UNVERSCHLÜSSELT
|
||||
- Header "X-HIN-Encrypted" für Steuerung
|
||||
|
||||
### AZA:
|
||||
- SMTP mit STARTTLS (opportunistisch, nicht erzwungen) – aza_email.py
|
||||
- IMAP über SSL (IMAP4_SSL) – aza_email.py
|
||||
- Keine S/MIME-Implementierung
|
||||
- Keine PGP/GPG-Implementierung
|
||||
- Keine Gateway-Verschlüsselung
|
||||
- Keine Ende-zu-Ende-Verschlüsselung
|
||||
- Keine Signierung von E-Mails
|
||||
- Passwörter im Klartext in aza_email_config.json gespeichert
|
||||
- Keine Zertifikatsverifikation im SMTP/IMAP-Code
|
||||
|
||||
### GAP:
|
||||
| Aspekt | HIN | AZA | Lücke |
|
||||
|--------|-----|-----|-------|
|
||||
| Mail-Verschlüsselung | S/MIME (RSA 4096) | Keine | JA |
|
||||
| Gateway-Verschlüsselung | Automatisch | Keine | JA |
|
||||
| Externe Empfänger | HIN Mail Global (GINA) | Keine Lösung | JA |
|
||||
| Signierung | S/MIME Signatur | Keine | JA |
|
||||
| Credential-Speicherung | UNBEKANNT | Klartext JSON | JA |
|
||||
| Zertifikatsverifikation | Domain-Zertifikate | Python-Defaults | JA |
|
||||
|
||||
### Kritikalität: **HIGH**
|
||||
|
||||
---
|
||||
|
||||
## OFFENE TECHNISCHE FRAGEN
|
||||
|
||||
1. HIN: TLS-Version, Cipher Suites, PFS – nicht in hin_docs belegt
|
||||
2. HIN: Hardware-Token / Smartcard – nicht in hin_docs belegt
|
||||
3. HIN: SSO-Protokoll (SAML/OAuth/OIDC) – nicht in hin_docs belegt
|
||||
4. HIN: Passwort-Hashing-Verfahren – nicht in hin_docs belegt
|
||||
5. HIN: Token-Ablaufzeiten – nicht in hin_docs belegt
|
||||
6. HIN: Credential-Speicherung – nicht in hin_docs belegt
|
||||
7. AZA: Python-Default-TLS-Verhalten hängt von Systemkonfiguration ab – nicht geprüft
|
||||
8. AZA: Supabase-Sicherheitskonfiguration – nicht im Scope dieser Analyse
|
||||
9. AZA: Ob STARTTLS erzwungen oder opportunistisch ist – Code zeigt keine Prüfung auf Erfolg
|
||||
|
||||
---
|
||||
|
||||
## ZUSAMMENFASSUNG
|
||||
|
||||
| Bereich | Kritikalität | Anzahl Lücken |
|
||||
|---------|-------------|---------------|
|
||||
| 1. Transport Security | HIGH | 5 |
|
||||
| 2. Netzwerkmodell | HIGH | 6 |
|
||||
| 3. PKI | HIGH | 5 |
|
||||
| 4. Authentifizierung | HIGH | 7 |
|
||||
| 5. Mail-Sicherheit | HIGH | 6 |
|
||||
| **GESAMT** | **HIGH** | **29 identifizierte Lücken** |
|
||||
|
||||
Alle 5 Bereiche haben Kritikalität HIGH.
|
||||
9 Punkte konnten mangels HIN-Dokumentation nicht vollständig verglichen werden.
|
||||
54
backup 24.2.26 - Kopie/security/handovers/STEP_03_SUMMARY.md
Normal file
54
backup 24.2.26 - Kopie/security/handovers/STEP_03_SUMMARY.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# STEP 3 – FORMELLE GAP-ANALYSE (HIN vs. AZA)
|
||||
# Status: ABGESCHLOSSEN
|
||||
|
||||
---
|
||||
|
||||
## Ziel des Schrittes
|
||||
|
||||
Formale Gegenüberstellung des HIN Security Stacks mit dem aktuellen
|
||||
AZA/MedWork-Sicherheitszustand. Nur faktenbasierter Vergleich,
|
||||
keine Empfehlungen, keine Implementierung.
|
||||
|
||||
## Konkrete Änderungen
|
||||
|
||||
- NEU: /security/handovers/STEP_03_GAP_ANALYSE.md (vollständige Gap-Analyse)
|
||||
|
||||
## Betroffene Dateien
|
||||
|
||||
Keine Dateien geändert. Nur Analyse-Dokument erstellt.
|
||||
|
||||
Analysierte Quelldateien:
|
||||
- basis14.py (Passwort-Hashing, Login)
|
||||
- aza_email.py (SMTP/IMAP, Mail-Sicherheit)
|
||||
- aza_email_config.json (Credential-Speicherung)
|
||||
- aza_config.py (Supabase-Zugriff)
|
||||
- backend_main.py (API-Token, User-Header)
|
||||
- todo_server.py (HTTP-Server, Firewall)
|
||||
- transcribe_server.py (kein TLS)
|
||||
- workforce_planner/api/auth.py (JWT, bcrypt)
|
||||
- workforce_planner/config.py (Secret Key)
|
||||
- workforce_planner/core/enums.py (Rollen)
|
||||
- workforce_planner/core/models.py (Employee-Modell)
|
||||
|
||||
## Sicherheitsauswirkung
|
||||
|
||||
29 Lücken identifiziert, alle 5 Bereiche mit Kritikalität HIGH:
|
||||
- Transport: 5 Lücken (kein S/MIME, kein verschlüsselter App-Kanal, HTTP-Server)
|
||||
- Netzwerk: 6 Lücken (kein Perimeter, kein VPN, kein DDoS-Schutz)
|
||||
- PKI: 5 Lücken (keine CA, keine Zertifikate, kein Schlüsselmanagement)
|
||||
- Authentifizierung: 7 Lücken (keine 2FA, SHA-256 ohne Salt, hardcoded Secret)
|
||||
- Mail: 6 Lücken (keine Verschlüsselung, Klartext-Passwörter in Config)
|
||||
|
||||
## Offene Punkte
|
||||
|
||||
- 9 Vergleichspunkte nicht vollständig bewertbar (HIN-Dokumentation fehlt)
|
||||
- Supabase-Sicherheitskonfiguration nicht im Scope
|
||||
- Python-Default-TLS-Verhalten systemabhängig
|
||||
|
||||
## Risiken
|
||||
|
||||
- Alle 5 Bereiche zeigen fundamentale Lücken zum HIN-Standard
|
||||
- Besonders kritisch: Klartext-Passwörter in aza_email_config.json
|
||||
- Besonders kritisch: SHA-256 ohne Salt in basis14.py
|
||||
- Besonders kritisch: Hardcoded Secret Key "dev-secret-change-in-production"
|
||||
- Besonders kritisch: HTTP-Server ohne TLS (todo_server, transcribe_server, backend)
|
||||
@@ -0,0 +1,139 @@
|
||||
# STEP 10 – Technische Einwilligungs-Protokollierung
|
||||
|
||||
## Ziel
|
||||
Jede KI-bezogene Datenverarbeitung muss technisch nachweisbar
|
||||
durch eine Einwilligung gedeckt sein.
|
||||
|
||||
---
|
||||
|
||||
## 1. Datenmodell
|
||||
|
||||
Jeder Consent-Eintrag enthält:
|
||||
|
||||
| Feld | Typ | Beschreibung |
|
||||
|---|---|---|
|
||||
| user_id | string | Benutzername aus Profil |
|
||||
| consent_type | string | Immer "ai_processing" |
|
||||
| consent_version | string | Stand-Datum aus ai_consent.md |
|
||||
| timestamp | string | UTC ISO-8601 |
|
||||
| source | string | "ui", "test", "admin" |
|
||||
| action | string | "grant" oder "revoke" |
|
||||
| prev_hash | string | SHA-256 des vorherigen Eintrags |
|
||||
| hash | string | SHA-256 dieses Eintrags (Integritaetskette) |
|
||||
|
||||
## 2. Speicherort
|
||||
|
||||
- Datei: `aza_consent_log.json` (Projekt-Root)
|
||||
- Format: JSON-Array (Append-only)
|
||||
- Manipulationssicherheit: SHA-256-Hash-Kette
|
||||
(jeder Eintrag referenziert den Hash des vorherigen)
|
||||
- Kein Überschreiben: Widerruf erzeugt neuen Eintrag,
|
||||
alter Grant bleibt in der Historie
|
||||
|
||||
## 3. Enforcement-Punkte
|
||||
|
||||
KI-Funktionen werden an folgenden Stellen blockiert, wenn
|
||||
keine gueltige Einwilligung vorliegt:
|
||||
|
||||
| Stelle | Datei | Methode |
|
||||
|---|---|---|
|
||||
| Chat Completion (alle KI-Texte) | basis14.py | call_chat_completion() |
|
||||
| Transkription | basis14.py | transcribe_wav() |
|
||||
| Aufnahme starten | basis14.py | toggle_record() |
|
||||
| Korrektur-Aufnahme | basis14.py | _toggle_record_append() |
|
||||
| Diktat in Widget | basis14.py | _diktat_into_widget() |
|
||||
| KI-Prüfung | basis14.py | open_ki_pruefen() |
|
||||
| Interaktionscheck | basis14.py | do_interaktion() |
|
||||
|
||||
Bei fehlendem Consent:
|
||||
- UI-Einstiegspunkte: Consent-Dialog wird angezeigt
|
||||
- API-Gateways: RuntimeError wird geworfen
|
||||
|
||||
## 4. UI-Elemente
|
||||
|
||||
### 4.1 Consent-Dialog (vor erster KI-Nutzung)
|
||||
- Zeigt den kompletten Text aus legal/ai_consent.md
|
||||
- Checkbox: "Ich habe den Text gelesen und stimme zu"
|
||||
- Buttons: "Zustimmen" / "Ablehnen"
|
||||
- Wird nur angezeigt, wenn kein gueltiger Consent vorliegt
|
||||
|
||||
### 4.2 Einstellungsfenster (Datenschutz & Recht)
|
||||
- Status-Anzeige: "Erteilt" / "Nicht erteilt / widerrufen"
|
||||
- Button: "KI-Einwilligung widerrufen" / "erteilen"
|
||||
- Button: "Consent-Log exportieren"
|
||||
- Bestehende Buttons: Datenschutzerklaerung, KI-Einwilligung
|
||||
|
||||
## 5. Versions-Handling
|
||||
|
||||
- Die Consent-Version wird aus dem "Stand:"-Feld in
|
||||
legal/ai_consent.md extrahiert
|
||||
- Bei Aenderung des Consent-Textes (neues Datum) wird
|
||||
ein bestehender Consent ungueltig
|
||||
- Benutzer muss erneut zustimmen
|
||||
|
||||
## 6. Audit-Workflow
|
||||
|
||||
### Export
|
||||
```
|
||||
python -c "from aza_consent import export_consent_log; print(export_consent_log())"
|
||||
```
|
||||
Oder ueber Einstellungen -> "Consent-Log exportieren"
|
||||
|
||||
### Integritaetspruefung
|
||||
```
|
||||
python -c "from aza_consent import verify_chain_integrity; ok, e = verify_chain_integrity(); print('OK' if ok else e)"
|
||||
```
|
||||
|
||||
### Export-Format
|
||||
```json
|
||||
{
|
||||
"export_timestamp": "2026-02-22T...",
|
||||
"total_entries": 3,
|
||||
"current_consent_version": "Februar 2026",
|
||||
"entries": [
|
||||
{
|
||||
"user_id": "...",
|
||||
"consent_type": "ai_processing",
|
||||
"consent_version": "Februar 2026",
|
||||
"timestamp": "2026-02-22T...",
|
||||
"source": "ui",
|
||||
"action": "grant",
|
||||
"prev_hash": "0000...0000",
|
||||
"hash": "a1b2c3..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 7. Geaenderte / Neue Dateien
|
||||
|
||||
| Datei | Aktion |
|
||||
|---|---|
|
||||
| aza_consent.py | NEU – Consent-Modul (Datenmodell, Storage, Enforcement, Export) |
|
||||
| basis14.py | GEAENDERT – Import aza_consent, Consent-Check an 7 Stellen |
|
||||
| aza_settings_mixin.py | GEAENDERT – Widerruf/Erteil-Button, Status, Export im Einstellungsfenster |
|
||||
| _test_consent.py | NEU – Testskript (14 Tests) |
|
||||
|
||||
## 8. Test-Ergebnisse (22.02.2026)
|
||||
|
||||
| Test | Ergebnis |
|
||||
|---|---|
|
||||
| Ohne Consent -> KI blockiert | PASS |
|
||||
| Consent erteilen -> KI erlaubt | PASS |
|
||||
| Widerruf -> KI blockiert | PASS |
|
||||
| Erneuter Consent -> KI erlaubt | PASS |
|
||||
| Hash-Kette intakt | PASS |
|
||||
| Status-Abfrage korrekt | PASS |
|
||||
| Version-Match | PASS |
|
||||
| Export erstellt gueltige Datei | PASS |
|
||||
| User-Historie korrekt | PASS |
|
||||
| Anderer User -> kein Consent | PASS |
|
||||
| Gesamt: 14/14 | ALLE BESTANDEN |
|
||||
|
||||
## 9. Risiken
|
||||
|
||||
| Risiko | Bewertung | Massnahme |
|
||||
|---|---|---|
|
||||
| JSON-Datei manuell editierbar | NIEDRIG | Hash-Kette erkennt Manipulation |
|
||||
| Kein digitaler Signatur-Mechanismus | MITTEL | Fuer Audit: Export + Integritaetspruefung |
|
||||
| Consent nur auf App-Ebene, nicht Patient | NIEDRIG | Papier-Einwilligung ergaenzt dies |
|
||||
@@ -0,0 +1,209 @@
|
||||
# STEP 10a – Consent Audit-Proof (Nachweis + Checkliste)
|
||||
|
||||
Datum: 22.02.2026
|
||||
Pruefung durch: Internes Audit-Skript (_test_consent_audit.py)
|
||||
|
||||
---
|
||||
|
||||
## 1. Speicherort + Schema
|
||||
|
||||
Datei: aza_consent_log.json (Projekt-Root)
|
||||
Format: JSON Array (Append-only, kein Ueberschreiben)
|
||||
|
||||
Schema pro Eintrag:
|
||||
|
||||
Feld Typ Beschreibung
|
||||
-----------------------------------------------
|
||||
user_id string Benutzer-ID aus Profil
|
||||
consent_type string Immer "ai_processing"
|
||||
consent_version string Stand-Datum aus ai_consent.md
|
||||
timestamp string UTC ISO-8601
|
||||
source string "ui" / "test" / "admin"
|
||||
action string "grant" oder "revoke"
|
||||
prev_hash string SHA-256 des vorherigen Eintrags
|
||||
hash string SHA-256 dieses Eintrags
|
||||
|
||||
Versions-Mechanismus:
|
||||
consent_version wird aus legal/ai_consent.md extrahiert.
|
||||
Die Funktion _get_consent_version() liest die Zeile, die mit
|
||||
"Stand:" beginnt, und gibt den Wert nach dem Doppelpunkt zurueck.
|
||||
Aktuell: "Februar 2026"
|
||||
Bei Aenderung des Datums in ai_consent.md wird jede bestehende
|
||||
Einwilligung ungueltig und muss erneuert werden.
|
||||
|
||||
---
|
||||
|
||||
## 2. Beispiel-Logeintraege (sanitized)
|
||||
|
||||
2a) GRANT (Zustimmung):
|
||||
|
||||
{
|
||||
"user_id": "user_1",
|
||||
"consent_type": "ai_processing",
|
||||
"consent_version": "Februar 2026",
|
||||
"timestamp": "2026-02-22T21:13:25.419060+00:00",
|
||||
"source": "ui",
|
||||
"action": "grant",
|
||||
"prev_hash": "000000000000000000000000000000000000000000000000...",
|
||||
"hash": "6988dcc3fb7215af69eb1fcdd35afaf7aab37262b3ed..."
|
||||
}
|
||||
|
||||
2b) REVOKE (Widerruf):
|
||||
|
||||
{
|
||||
"user_id": "user_1",
|
||||
"consent_type": "ai_processing",
|
||||
"consent_version": "Februar 2026",
|
||||
"timestamp": "2026-02-22T21:13:25.419060+00:00",
|
||||
"source": "ui",
|
||||
"action": "revoke",
|
||||
"prev_hash": "6988dcc3fb7215af69eb1fcdd35afaf7aab37262b3ed...",
|
||||
"hash": "2a60ef4a43cefc8e3c39dc155530c0f443bbb0b47b0e..."
|
||||
}
|
||||
|
||||
2c) RE-GRANT (erneute Zustimmung):
|
||||
|
||||
{
|
||||
"user_id": "user_1",
|
||||
"consent_type": "ai_processing",
|
||||
"consent_version": "Februar 2026",
|
||||
"timestamp": "2026-02-22T21:13:25.421171+00:00",
|
||||
"source": "ui",
|
||||
"action": "grant",
|
||||
"prev_hash": "2a60ef4a43cefc8e3c39dc155530c0f443bbb0b47b0e...",
|
||||
"hash": "2cb429a75c3d02f8861c6fd6fbec1216acb6a453926a..."
|
||||
}
|
||||
|
||||
Timestamp-Pruefung (UTC-Nachweis):
|
||||
Alle Timestamps enden auf "+00:00" -> UTC bestaetigt.
|
||||
|
||||
---
|
||||
|
||||
## 3. Integritaetsbeweis
|
||||
|
||||
3a) Intakte Log-Datei:
|
||||
|
||||
verify_chain_integrity() -> PASS
|
||||
Alle Hash-Werte stimmen mit der Berechnung ueberein.
|
||||
Alle prev_hash-Referenzen sind korrekt verkettet.
|
||||
|
||||
3b) Manipulierte Log-Datei:
|
||||
|
||||
Manipulation: timestamp von Eintrag 0 um ein Zeichen geaendert
|
||||
("...+00:00" -> "...+00:0X")
|
||||
|
||||
verify_chain_integrity() -> FAIL
|
||||
Fehlermeldung:
|
||||
"Eintrag 0: Hash stimmt nicht
|
||||
(erwartet 8b92439ca6d1b926..., gefunden 6988dcc3fb7215af...)"
|
||||
|
||||
Ergebnis: Manipulation wird zuverlaessig erkannt.
|
||||
|
||||
3c) Nach Restore der Originaldatei:
|
||||
|
||||
verify_chain_integrity() -> PASS
|
||||
|
||||
---
|
||||
|
||||
## 4. Enforcement-Beweis
|
||||
|
||||
4a) Ohne Consent:
|
||||
has_valid_consent("user_1") -> False
|
||||
KI-Funktion: BLOCKIERT
|
||||
Verhalten: RuntimeError "KI-Einwilligung fehlt oder wurde widerrufen."
|
||||
UI: Consent-Dialog wird angezeigt (Checkbox + Zustimmen-Button)
|
||||
|
||||
4b) Nach Consent:
|
||||
record_consent("user_1") ausgefuehrt
|
||||
has_valid_consent("user_1") -> True
|
||||
KI-Funktion: ERLAUBT
|
||||
|
||||
4c) Nach Widerruf:
|
||||
record_revoke("user_1") ausgefuehrt
|
||||
has_valid_consent("user_1") -> False
|
||||
KI-Funktion: BLOCKIERT
|
||||
|
||||
4d) Version-Change (consent_version geaendert):
|
||||
Vor Aenderung: has_valid_consent -> True
|
||||
consent_version im Log manuell auf "Januar 2025" gesetzt
|
||||
Aktuelle Version in ai_consent.md: "Februar 2026"
|
||||
has_valid_consent -> False
|
||||
Ergebnis: Erneute Zustimmung erforderlich (KORREKT)
|
||||
|
||||
Enforcement-Punkte im Code:
|
||||
|
||||
Datei Methode Typ
|
||||
-------------------------------------------------------
|
||||
basis14.py call_chat_completion() API-Gateway
|
||||
basis14.py transcribe_wav() API-Gateway
|
||||
basis14.py toggle_record() UI-Einstieg
|
||||
basis14.py _toggle_record_append() UI-Einstieg
|
||||
basis14.py _diktat_into_widget() UI-Einstieg
|
||||
basis14.py open_ki_pruefen() UI-Einstieg
|
||||
basis14.py do_interaktion() UI-Einstieg
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Minimization Check
|
||||
|
||||
Gespeicherte Felder im Consent-Log (vollstaendige Liste):
|
||||
|
||||
action, consent_type, consent_version, hash,
|
||||
prev_hash, source, timestamp, user_id
|
||||
|
||||
Pruefung auf sensible Daten:
|
||||
|
||||
Transkript-Inhalte: NICHT gespeichert
|
||||
KG-Inhalte: NICHT gespeichert
|
||||
Prompts / KI-Antworten: NICHT gespeichert
|
||||
API-Keys / Secrets: NICHT gespeichert
|
||||
Passwoerter: NICHT gespeichert
|
||||
Patientennamen: NICHT gespeichert
|
||||
Diagnosen: NICHT gespeichert
|
||||
Audio-Daten: NICHT gespeichert
|
||||
|
||||
Kein Feldwert ueberschreitet 200 Zeichen
|
||||
(ausser hash/prev_hash mit genau 64 Hex-Zeichen).
|
||||
|
||||
Data Minimization: PASS
|
||||
|
||||
---
|
||||
|
||||
## 6. Gesamtbewertung
|
||||
|
||||
Pruefpunkt Ergebnis
|
||||
-------------------------------------------------------
|
||||
Speicherort + Schema PASS
|
||||
Beispiel-Logeintraege PASS
|
||||
Timestamp UTC PASS
|
||||
Integritaet intakt PASS
|
||||
Integritaet manipuliert erkannt PASS
|
||||
Enforcement ohne Consent PASS
|
||||
Enforcement mit Consent PASS
|
||||
Enforcement nach Widerruf PASS
|
||||
Version-Change erfordert Neu-Consent PASS
|
||||
Data Minimization PASS
|
||||
|
||||
GESAMTBEWERTUNG: PASS (10/10)
|
||||
|
||||
---
|
||||
|
||||
## 7. Reproduzierbarkeit
|
||||
|
||||
Der Nachweis kann jederzeit reproduziert werden mit:
|
||||
|
||||
python _test_consent_audit.py
|
||||
|
||||
Das Skript erzeugt temporaere Testdaten, fuehrt alle Pruefungen
|
||||
durch und raeumt danach auf (keine persistenten Aenderungen).
|
||||
|
||||
---
|
||||
|
||||
## 8. Offene Punkte
|
||||
|
||||
- Kein digitaler Signatur-Mechanismus (nur Hash-Kette):
|
||||
Fuer hoechste Anforderungen waere eine kryptografische
|
||||
Signatur (z.B. RSA/ECDSA) empfehlenswert.
|
||||
- Consent bezieht sich auf den Anwendungsbenutzer (Arzt),
|
||||
nicht auf den Patienten. Die Patienten-Einwilligung erfolgt
|
||||
ueber das Papierformular (legal/ai_consent.md).
|
||||
@@ -0,0 +1,151 @@
|
||||
# STEP 11 – Audit-Logging (Minimal & DSG-konform)
|
||||
|
||||
## Ziel
|
||||
Sicherheitsrelevante Ereignisse nachvollziehbar protokollieren,
|
||||
ohne sensible Daten (Patientendaten, Prompts, Passwoerter) zu speichern.
|
||||
|
||||
---
|
||||
|
||||
## 1. Architektur
|
||||
|
||||
Neues Modul: aza_audit_log.py
|
||||
|
||||
Log-Format: Pipe-separierte Textdatei (eine Zeile pro Ereignis)
|
||||
|
||||
TIMESTAMP | EVENT | USER | STATUS | SOURCE | DETAIL
|
||||
|
||||
Beispiel:
|
||||
2026-02-22T21:13:25.419+00:00 | LOGIN_OK | Dr. Mueller | OK | desktop |
|
||||
|
||||
Alle Timestamps sind UTC (ISO-8601).
|
||||
|
||||
---
|
||||
|
||||
## 2. Protokollierte Ereignisse
|
||||
|
||||
Event-Typ Beschreibung Wann
|
||||
------------------------------------------------------------------
|
||||
APP_START Anwendung gestartet main()
|
||||
APP_STOP Anwendung beendet nach mainloop()
|
||||
LOGIN_OK Erfolgreicher Login do_login()
|
||||
LOGIN_FAIL Fehlgeschlagener Login do_login()
|
||||
2FA_OK 2FA-Verifizierung erfolgreich do_verify()
|
||||
2FA_FAIL 2FA-Verifizierung fehlgeschlagen do_verify()
|
||||
CONSENT_GRANT KI-Einwilligung erteilt on_accept()
|
||||
CONSENT_REVOKE KI-Einwilligung widerrufen toggle_consent()
|
||||
AI_TRANSCRIBE KI-Transkription gestartet transcribe_wav()
|
||||
AI_CHAT KI-Chat-Completion aufgerufen call_chat_completion()
|
||||
AI_BLOCKED KI-Aufruf ohne Consent blockiert transcribe/chat
|
||||
PASSWORD_REHASH Legacy-Hash auf bcrypt migriert do_login()
|
||||
EXPORT Log-Export durchgefuehrt do_export()
|
||||
|
||||
---
|
||||
|
||||
## 3. Data Minimization (DSG Art. 6)
|
||||
|
||||
Was NICHT im Audit-Log gespeichert wird:
|
||||
- Patientennamen / Patientendaten
|
||||
- Transkripte / KG-Inhalte
|
||||
- KI-Prompts / KI-Antworten
|
||||
- Passwoerter / Passwort-Hashes
|
||||
- API-Keys / Secrets
|
||||
- E-Mail-Inhalte
|
||||
|
||||
Detail-Feld: Maximal 200 Zeichen, nur Metadaten
|
||||
(z.B. "model=gpt-5.2", "2FA backup-code").
|
||||
|
||||
---
|
||||
|
||||
## 4. Log-Rotation
|
||||
|
||||
Parameter Standard ENV-Variable
|
||||
--------------------------------------------------
|
||||
Max. Dateigroesse 10 MB AZA_AUDIT_ROTATE_MB
|
||||
Rotierte Dateien 12 AZA_AUDIT_KEEP
|
||||
Logdatei-Pfad aza_audit.log AZA_AUDIT_LOG
|
||||
|
||||
Bei Ueberschreitung der Maximalgroesse:
|
||||
aza_audit.log -> aza_audit.1.log -> ... -> aza_audit.12.log
|
||||
Aelteste Datei wird ueberschrieben.
|
||||
|
||||
---
|
||||
|
||||
## 5. Export
|
||||
|
||||
Ueber Einstellungen -> "Logs exportieren (Audit)":
|
||||
Exportiert sowohl Consent-Log als auch Audit-Log als JSON.
|
||||
|
||||
Programmatisch:
|
||||
python -c "from aza_audit_log import export_audit_log; print(export_audit_log())"
|
||||
|
||||
Export-Format:
|
||||
{
|
||||
"export_timestamp": "2026-02-22T...",
|
||||
"total_entries": 12,
|
||||
"source_file": ".../aza_audit.log",
|
||||
"entries": [
|
||||
{
|
||||
"timestamp": "2026-02-22T...",
|
||||
"event": "LOGIN_OK",
|
||||
"user_id": "...",
|
||||
"status": "OK",
|
||||
"source": "desktop",
|
||||
"detail": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
## 6. Integration bestehender Audit-Systeme
|
||||
|
||||
Komponente Audit-System Speicherort
|
||||
-----------------------------------------------------------
|
||||
Desktop-App (basis14) aza_audit_log.py aza_audit.log
|
||||
Backend (backend_main) _audit_write() medwork_audit.log
|
||||
Workforce Planner log_action() + DB workforce_planner.db
|
||||
|
||||
Alle drei Systeme sind unabhaengig und koennen
|
||||
separat exportiert werden.
|
||||
|
||||
---
|
||||
|
||||
## 7. Geaenderte / Neue Dateien
|
||||
|
||||
Datei Aktion
|
||||
--------------------------------------------------
|
||||
aza_audit_log.py NEU – Audit-Log-Modul
|
||||
basis14.py GEAENDERT – Import + 10 log_event()-Aufrufe
|
||||
aza_settings_mixin.py GEAENDERT – Import + Widerruf-Log + Export
|
||||
_test_audit_log.py NEU – Testskript (23 Tests)
|
||||
|
||||
---
|
||||
|
||||
## 8. Test-Ergebnisse (22.02.2026)
|
||||
|
||||
Test Ergebnis
|
||||
-------------------------------------------
|
||||
Events schreiben PASS
|
||||
12 Zeilen korrekt PASS
|
||||
6 Felder pro Zeile PASS
|
||||
Timestamp UTC PASS
|
||||
Event-Typ korrekt PASS
|
||||
User korrekt PASS
|
||||
Status OK/FAIL korrekt PASS
|
||||
Source = desktop PASS
|
||||
Statistiken korrekt PASS
|
||||
Export JSON korrekt PASS
|
||||
Data Minimization PASS
|
||||
Pipe-Sanitierung PASS
|
||||
Gesamt: 23/23 ALLE BESTANDEN
|
||||
|
||||
---
|
||||
|
||||
## 9. Risiken
|
||||
|
||||
Risiko Bewertung Massnahme
|
||||
--------------------------------------------------------
|
||||
Log-Datei loeschbar NIEDRIG Backup sichert mit
|
||||
Kein zentrales SIEM MITTEL Export fuer manuelle Pruefung
|
||||
Keine Echtzeit-Alarmierung MITTEL Manueller Review empfohlen
|
||||
Log-Rotation verliert alte Daten NIEDRIG 12 Dateien a 10 MB = 120 MB
|
||||
@@ -0,0 +1,197 @@
|
||||
STEP 11a – AUDIT-LOG INTEGRITAET (HASH-KETTE)
|
||||
Datum: 2026-02-22
|
||||
Status: ABGESCHLOSSEN
|
||||
|
||||
=====================================================================
|
||||
1. ZIEL
|
||||
=====================================================================
|
||||
|
||||
Audit-Log (aza_audit_log.py) manipulations-erkennbar machen
|
||||
durch SHA-256-Hash-Kette, analog zum Consent-Log.
|
||||
|
||||
=====================================================================
|
||||
2. AENDERUNGEN
|
||||
=====================================================================
|
||||
|
||||
Datei: aza_audit_log.py
|
||||
|
||||
a) Neues Format (8 Felder statt 6):
|
||||
TIMESTAMP | EVENT | USER | STATUS | SOURCE | DETAIL | PREV_HASH | ENTRY_HASH
|
||||
|
||||
b) Hash-Berechnung:
|
||||
entry_hash = SHA-256(prev_hash + payload)
|
||||
payload = "TS | EVENT | USER | STATUS | SOURCE | DETAIL"
|
||||
Startwert (GENESIS): 64x "0"
|
||||
|
||||
c) Rotation mit Ketten-Uebergabe:
|
||||
Bei Rotation wird der letzte Hash des rotierten Files
|
||||
als Header in der neuen Datei gespeichert:
|
||||
#CHAIN_FROM=<letzter_entry_hash>
|
||||
Neue Eintraege setzen prev_hash = Chain-Header-Hash.
|
||||
Ein Auditor kann die Kette ueber Dateigrenzen pruefen.
|
||||
|
||||
d) Neue Funktionen:
|
||||
- verify_integrity(path) -> (bool, list[str])
|
||||
Prueft eine einzelne Logdatei auf Ketten-Integritaet.
|
||||
- verify_all_rotations() -> (bool, dict)
|
||||
Prueft alle rotierten Dateien.
|
||||
|
||||
e) CLI-Interface:
|
||||
python -m aza_audit_log verify -> Aktuelle Datei pruefen
|
||||
python -m aza_audit_log verify --all -> Alle Rotationsdateien
|
||||
python -m aza_audit_log verify --file X -> Bestimmte Datei
|
||||
python -m aza_audit_log stats -> Statistiken + Integritaet
|
||||
python -m aza_audit_log export -> JSON-Export mit Integritaet
|
||||
|
||||
f) Export enthaelt jetzt:
|
||||
- integrity: PASS/FAIL
|
||||
- integrity_errors: []
|
||||
- Jeder Entry hat prev_hash und entry_hash
|
||||
|
||||
g) get_log_stats() enthaelt jetzt:
|
||||
- integrity: PASS/FAIL
|
||||
|
||||
=====================================================================
|
||||
3. ROTATIONS-STRATEGIE
|
||||
=====================================================================
|
||||
|
||||
Ablauf bei Rotation (Datei > AZA_AUDIT_ROTATE_MB):
|
||||
|
||||
1. Letzten entry_hash der aktuellen Datei auslesen
|
||||
2. Bestehende .1, .2, ... Dateien hochschieben
|
||||
3. Aktuelle Datei -> .1 verschieben
|
||||
4. Neue leere Datei anlegen mit Header:
|
||||
#CHAIN_FROM=<letzter_hash>
|
||||
5. Neue Eintraege nutzen diesen Hash als prev_hash
|
||||
|
||||
Verifizierung:
|
||||
- verify_integrity() pro Datei: prueft interne Kette
|
||||
- Header #CHAIN_FROM verbindet die Dateien logisch
|
||||
- verify_all_rotations() prueft alle Dateien der Reihe nach
|
||||
|
||||
=====================================================================
|
||||
4. BEISPIEL LOG-ZEILE
|
||||
=====================================================================
|
||||
|
||||
2026-02-22T21:42:27.160+00:00 | APP_START | user_1 | OK | desktop | test | 000000...0000 | a3f8e1...
|
||||
|
||||
Felder:
|
||||
[0] Timestamp (UTC, ISO-8601)
|
||||
[1] Event-Typ
|
||||
[2] User-ID
|
||||
[3] Status (OK/FAIL)
|
||||
[4] Source
|
||||
[5] Detail (max 200 Zeichen, keine sensiblen Daten)
|
||||
[6] prev_hash (SHA-256 des vorherigen Eintrags)
|
||||
[7] entry_hash (SHA-256 ueber prev_hash + Felder 0-5)
|
||||
|
||||
=====================================================================
|
||||
5. VERIFY-ANLEITUNG + BEISPIELAUSGABEN
|
||||
=====================================================================
|
||||
|
||||
a) Integritaet pruefen (CLI):
|
||||
|
||||
$ python -m aza_audit_log verify
|
||||
Datei: C:\...\aza_audit.log
|
||||
Integritaet: PASS
|
||||
|
||||
b) Manipulation erkennen:
|
||||
|
||||
$ python -m aza_audit_log verify
|
||||
Datei: C:\...\aza_audit.log
|
||||
Integritaet: FAIL
|
||||
Zeile 2: entry_hash stimmt nicht (erwartet 104ebe9a..., gefunden 32e00f09...)
|
||||
|
||||
c) Alle Rotationsdateien:
|
||||
|
||||
$ python -m aza_audit_log verify --all
|
||||
aza_audit.1.log: PASS
|
||||
aza_audit.log: PASS
|
||||
GESAMT: PASS
|
||||
|
||||
d) Statistiken:
|
||||
|
||||
$ python -m aza_audit_log stats
|
||||
Datei: aza_audit.log
|
||||
Existiert: True
|
||||
Groesse: 0.0 MB
|
||||
Eintraege: 2
|
||||
Integritaet: PASS
|
||||
|
||||
=====================================================================
|
||||
6. TEST-ERGEBNIS (PROOF)
|
||||
=====================================================================
|
||||
|
||||
Testskript: _test_audit_integrity.py
|
||||
21 Tests, 0 Fehler.
|
||||
|
||||
Tests:
|
||||
1. Hash-Kette schreiben + verifizieren:
|
||||
- 5 Eintraege geschrieben, verify PASS
|
||||
- 8 Felder pro Zeile
|
||||
- prev_hash[0] = GENESIS
|
||||
- prev_hash[n] = entry_hash[n-1]
|
||||
|
||||
2. Manipulation erkennen:
|
||||
- 1 Zeichen geaendert (LOGIN_OK -> LOGIN_XX)
|
||||
- verify -> FAIL, Fehlerstelle gemeldet
|
||||
- Restore -> PASS
|
||||
|
||||
3. Rotation mit Ketten-Uebergabe:
|
||||
- Rotierte Datei (.1) intakt: PASS
|
||||
- Aktuelle Datei mit Chain-Header: PASS
|
||||
- Header-Hash == letzter Hash der rotierten Datei: PASS
|
||||
- prev_hash im neuen File == Chain-Header: PASS
|
||||
- verify_all_rotations: PASS
|
||||
|
||||
4. Stats + Export:
|
||||
- Integritaet in Stats: PASS
|
||||
- Export integrity: PASS
|
||||
- Entries mit entry_hash: PASS
|
||||
|
||||
5. Data Minimization:
|
||||
- Kein Passwort, kein API-Key, kein Transkript: PASS
|
||||
|
||||
=====================================================================
|
||||
7. DATA MINIMIZATION
|
||||
=====================================================================
|
||||
|
||||
Unveraendert gegenueber STEP 11:
|
||||
- Keine Patientennamen
|
||||
- Keine Transkripte/Prompts
|
||||
- Keine Passwoerter/API-Keys
|
||||
- Detail max. 200 Zeichen, nur Metadaten
|
||||
|
||||
=====================================================================
|
||||
8. BETROFFENE DATEIEN
|
||||
=====================================================================
|
||||
|
||||
Geaendert:
|
||||
- aza_audit_log.py (Format erweitert, Hash-Kette, verify, CLI)
|
||||
|
||||
Neu:
|
||||
- _test_audit_integrity.py (Proof-Skript)
|
||||
- security/handovers/STEP_11a_AUDIT_LOG_INTEGRITY.md (dieses Dokument)
|
||||
|
||||
Nicht geaendert:
|
||||
- basis14.py (Aufrufe bleiben kompatibel, neue Felder transparent)
|
||||
- aza_settings_mixin.py (unveraendert)
|
||||
- aza_consent.py (unveraendert)
|
||||
|
||||
=====================================================================
|
||||
9. RISIKEN
|
||||
=====================================================================
|
||||
|
||||
- Performance: _get_last_hash() liest die gesamte Datei.
|
||||
Bei sehr grossen Dateien (kurz vor Rotation) kann dies langsam sein.
|
||||
Akzeptabel, da Rotation bei 10 MB greift.
|
||||
|
||||
- Rueckwaertskompatibilitaet: Bestehende 6-Feld-Logdateien werden
|
||||
von verify_integrity() als ungueltig gemeldet (< 8 Felder).
|
||||
Loesung: Bestehende Logdatei vor erstem Start archivieren/loeschen.
|
||||
|
||||
=====================================================================
|
||||
10. OFFENE PUNKTE
|
||||
=====================================================================
|
||||
|
||||
Keine.
|
||||
211
backup 24.2.26 - Kopie/security/handovers/STEP_12_MONITORING.md
Normal file
211
backup 24.2.26 - Kopie/security/handovers/STEP_12_MONITORING.md
Normal file
@@ -0,0 +1,211 @@
|
||||
STEP 12 – MONITORING & HEALTH CHECKS (MINIMAL)
|
||||
Datum: 2026-02-22
|
||||
Status: ABGESCHLOSSEN
|
||||
|
||||
=====================================================================
|
||||
1. ZIEL
|
||||
=====================================================================
|
||||
|
||||
Minimaler Betriebsnachweis:
|
||||
- Services leben (Health-Checks)
|
||||
- Metriken aus bestehenden Logs (kein externer Dienst)
|
||||
- Integritaetspruefung automatisierbar
|
||||
- Scheduling dokumentiert
|
||||
|
||||
Keine Patientendaten erfasst.
|
||||
|
||||
=====================================================================
|
||||
2. HEALTH-CHECK ENDPOINTS
|
||||
=====================================================================
|
||||
|
||||
Alle Services liefern jetzt unter /health:
|
||||
|
||||
{
|
||||
"status": "ok",
|
||||
"version": "<app-version>",
|
||||
"uptime_s": <seconds>,
|
||||
"tls": true/false
|
||||
}
|
||||
|
||||
a) backend_main.py
|
||||
URL: https://127.0.0.1:8000/health
|
||||
Vorher: {"ok": true}
|
||||
Nachher: {"status": "ok", "version": "0.1.0", "uptime_s": ..., "tls": ...}
|
||||
|
||||
b) transcribe_server.py
|
||||
URL: https://127.0.0.1:8090/health
|
||||
Vorher: {"status": "ok"}
|
||||
Nachher: {"status": "ok", "version": "0.1.0", "uptime_s": ..., "tls": ...}
|
||||
|
||||
c) todo_server.py
|
||||
URL: https://127.0.0.1:5111/health
|
||||
Vorher: Kein /health Endpoint
|
||||
Nachher: {"status": "ok", "version": "1.0.0", "uptime_s": ..., "tls": ...}
|
||||
|
||||
d) Desktop-App (basis14.py)
|
||||
Kein HTTP-Endpoint (Desktop-App ohne eigenen Server).
|
||||
Betriebsnachweis ueber Audit-Log:
|
||||
- APP_START / APP_STOP Events
|
||||
- LOGIN_OK / LOGIN_FAIL Events
|
||||
|
||||
=====================================================================
|
||||
3. MONITORING-METRIKEN (aza_monitoring.py)
|
||||
=====================================================================
|
||||
|
||||
Quellen: Nur bestehende lokale Logs/Dateien. Kein Cloud-Dienst.
|
||||
|
||||
a) Audit-Log Metriken (aus aza_audit_log.py):
|
||||
- Anzahl LOGIN_FAIL
|
||||
- Anzahl AI_CHAT + AI_TRANSCRIBE (KI-Calls Zaehler)
|
||||
- Anzahl AI_BLOCKED
|
||||
- Anzahl 2FA_FAIL
|
||||
- Integritaets-Status (PASS/FAIL)
|
||||
|
||||
b) Consent-Log Metriken (aus aza_consent.py):
|
||||
- Anzahl Eintraege
|
||||
- Integritaets-Status
|
||||
|
||||
c) Backup-Metriken (aus backups/ Verzeichnis):
|
||||
- Anzahl vorhandener Backups
|
||||
- Letztes Backup (Name + Zeitpunkt)
|
||||
|
||||
d) Alert-Severity-Stufen:
|
||||
- INFO: Zaehler ohne Handlungsbedarf
|
||||
- WARN: Erhoehte Werte (z.B. > 0 login_fail)
|
||||
- HIGH: Kritische Schwellen (z.B. >= 10 login_fail)
|
||||
- CRITICAL: Integritaets-Verletzung
|
||||
|
||||
=====================================================================
|
||||
4. INTEGRITAETS-CHECKS
|
||||
=====================================================================
|
||||
|
||||
Automatisierte Pruefung ueber:
|
||||
python aza_monitoring.py integrity
|
||||
|
||||
Prueft:
|
||||
- aza_audit_log: verify_all_rotations() (SHA-256 Hash-Kette)
|
||||
- aza_consent_log: verify_chain_integrity() (SHA-256 Hash-Kette)
|
||||
|
||||
Bei Fehler:
|
||||
- Klarer Log-Eintrag (INTEGRITY_FAIL Event ins Audit-Log)
|
||||
- Exit-Code 1 (fuer Scheduler-Alerting)
|
||||
|
||||
=====================================================================
|
||||
5. CLI-KOMMANDOS
|
||||
=====================================================================
|
||||
|
||||
python aza_monitoring.py health Health-Checks aller Services
|
||||
python aza_monitoring.py metrics Metriken aus Logs
|
||||
python aza_monitoring.py integrity Integritaetspruefung (Exit 0/1)
|
||||
python aza_monitoring.py alerts Sicherheits-Alerts
|
||||
python aza_monitoring.py nightly Naechtlicher Gesamtcheck + JSON-Report
|
||||
python aza_monitoring.py all Alles anzeigen
|
||||
|
||||
=====================================================================
|
||||
6. SCHEDULING-BEISPIELE
|
||||
=====================================================================
|
||||
|
||||
a) Linux (cron):
|
||||
|
||||
# Naechtlicher Gesamtcheck um 02:00
|
||||
0 2 * * * cd /pfad/zu/aza && python aza_monitoring.py nightly >> /var/log/aza_monitoring.log 2>&1
|
||||
|
||||
# Stuendlicher Health-Check
|
||||
0 * * * * cd /pfad/zu/aza && python aza_monitoring.py health >> /var/log/aza_health.log 2>&1
|
||||
|
||||
# Integritaet alle 6 Stunden
|
||||
0 */6 * * * cd /pfad/zu/aza && python aza_monitoring.py integrity || echo "INTEGRITY FAIL" | mail admin@example.com
|
||||
|
||||
b) Windows (Task Scheduler):
|
||||
|
||||
Aktion: Programm starten
|
||||
Programm: python
|
||||
Argumente: aza_monitoring.py nightly
|
||||
Starten in: C:\Users\surov\Documents\AZA\backup 19.2.26
|
||||
|
||||
Trigger: Taeglich, 02:00 Uhr
|
||||
|
||||
Alternativ via PowerShell:
|
||||
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute "python" `
|
||||
-Argument "aza_monitoring.py nightly" `
|
||||
-WorkingDirectory "C:\Users\surov\Documents\AZA\backup 19.2.26"
|
||||
$trigger = New-ScheduledTaskTrigger -Daily -At "02:00"
|
||||
Register-ScheduledTask -TaskName "AZA Nightly Monitor" `
|
||||
-Action $action -Trigger $trigger -Description "AZA Nightly Monitoring"
|
||||
|
||||
=====================================================================
|
||||
7. DATENSCHUTZ-HINWEIS (DATA MINIMIZATION)
|
||||
=====================================================================
|
||||
|
||||
Das Monitoring erfasst KEINE:
|
||||
- Patientennamen oder -daten
|
||||
- Transkript-Inhalte oder Prompts
|
||||
- Passwoerter oder API-Keys
|
||||
- KI-Antworten
|
||||
|
||||
Es werden ausschliesslich Zaehler und Status-Informationen erhoben:
|
||||
- Anzahl Events pro Typ
|
||||
- PASS/FAIL Status
|
||||
- Dateigeroessen und Zeitstempel
|
||||
- Service-Version und Uptime
|
||||
|
||||
Health-Check-Responses enthalten nur technische Metadaten.
|
||||
|
||||
=====================================================================
|
||||
8. TEST-ERGEBNIS
|
||||
=====================================================================
|
||||
|
||||
Testskript: _test_monitoring.py
|
||||
27 Tests, 0 Fehler.
|
||||
|
||||
Tests:
|
||||
1. Metriken: Audit-Log Eintraege, Integritaet, Backup-Count
|
||||
2. Alerts: login_fail, ai_calls_total, ai_blocked, 2fa_fail erkannt
|
||||
3. Integritaets-Check: PASS bei intaktem Log
|
||||
4. Manipulation: FAIL bei manipuliertem Log, PASS nach Restore
|
||||
5. Nightly-Report: JSON-Struktur korrekt, Datei geschrieben
|
||||
6. Data Minimization: Keine Passwoerter/Keys/Transkripte
|
||||
7. Health-Check Format: _APP_VERSION und _START_TIME vorhanden
|
||||
|
||||
=====================================================================
|
||||
9. BETROFFENE DATEIEN
|
||||
=====================================================================
|
||||
|
||||
Geaendert:
|
||||
- backend_main.py (/health erweitert: version, uptime_s, tls)
|
||||
- transcribe_server.py (/health erweitert: version, uptime_s, tls)
|
||||
- todo_server.py (/health neu hinzugefuegt)
|
||||
|
||||
Neu:
|
||||
- aza_monitoring.py (Monitoring, Metriken, Integrity, CLI)
|
||||
- _test_monitoring.py (Proof-Skript)
|
||||
|
||||
Nicht geaendert:
|
||||
- basis14.py
|
||||
- aza_audit_log.py (unveraendert, wird nur gelesen)
|
||||
- aza_consent.py (unveraendert, wird nur gelesen)
|
||||
|
||||
=====================================================================
|
||||
10. RISIKEN
|
||||
=====================================================================
|
||||
|
||||
- Health-Checks sind unauthentifiziert.
|
||||
Risiko: Gering (nur Status-Info, keine sensiblen Daten).
|
||||
Massnahme: Bei Bedarf hinter API-Token schuetzen.
|
||||
|
||||
- Monitoring laeuft lokal, kein externes Alerting.
|
||||
Risiko: Alerts werden nur im JSON-Report gespeichert.
|
||||
Massnahme: Nightly-Report per Mail weiterleiten (Empfehlung).
|
||||
|
||||
- Kein Real-Time-Monitoring.
|
||||
Risiko: Zwischenzeitliche Ausfaelle werden erst beim naechsten
|
||||
Scheduled-Check erkannt.
|
||||
Massnahme: Health-Check-Intervall anpassen (z.B. alle 5 Min).
|
||||
|
||||
=====================================================================
|
||||
11. OFFENE PUNKTE
|
||||
=====================================================================
|
||||
|
||||
Keine.
|
||||
100
backup 24.2.26 - Kopie/security/handovers/STEP_4_1_TLS.md
Normal file
100
backup 24.2.26 - Kopie/security/handovers/STEP_4_1_TLS.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# STEP 4.1 – TLS-PFLICHT FÜR ALLE BACKEND-SERVICES
|
||||
# Status: ABGESCHLOSSEN
|
||||
|
||||
---
|
||||
|
||||
## Ziel
|
||||
|
||||
GAP #4: Alle Backend-Services von HTTP auf HTTPS/TLS umstellen.
|
||||
Fail-Start bei fehlendem Zertifikat im Produktionsmodus.
|
||||
|
||||
## Geänderte Dateien
|
||||
|
||||
| Datei | Änderung |
|
||||
|-------|----------|
|
||||
| **aza_tls.py** | NEU – Zentrale TLS-Konfiguration |
|
||||
| **todo_server.py** | TLS-Integration via create_ssl_context() |
|
||||
| **transcribe_server.py** | TLS-Integration via uvicorn SSL-Kwargs |
|
||||
| **backend_main.py** | TLS-Integration via uvicorn SSL-Kwargs |
|
||||
|
||||
## TLS-Parameter
|
||||
|
||||
| Parameter | Wert |
|
||||
|-----------|------|
|
||||
| Minimale TLS-Version | 1.2 (konfigurierbar via ENV) |
|
||||
| Maximale TLS-Version | 1.3 |
|
||||
| Cipher Suites | ECDHE+AESGCM, ECDHE+CHACHA20, DHE+AESGCM, DHE+CHACHA20 |
|
||||
| Ausgeschlossen | aNULL, eNULL, MD5, DSS, RC4, 3DES |
|
||||
| Perfect Forward Secrecy | Ja (nur ECDHE/DHE Cipher erlaubt) |
|
||||
| DEV-Zertifikat | RSA 4096-bit, SHA-256, 365 Tage, Self-Signed |
|
||||
| PROD-Vorbereitung | Pfade über ENV-Variablen konfigurierbar (ACME/Let's Encrypt) |
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
| Variable | Default | Beschreibung |
|
||||
|----------|---------|--------------|
|
||||
| AZA_TLS_CERTFILE | (leer) | Pfad zum Zertifikat (PEM) |
|
||||
| AZA_TLS_KEYFILE | (leer) | Pfad zum Private Key (PEM) |
|
||||
| AZA_TLS_MIN_VERSION | "1.2" | Minimale TLS-Version ("1.2" oder "1.3") |
|
||||
| AZA_TLS_REQUIRE | "1" | "1" = Fail-Start ohne Zertifikat |
|
||||
|
||||
## Betroffene Server
|
||||
|
||||
| Server | Port | Typ | TLS-Methode |
|
||||
|--------|------|-----|-------------|
|
||||
| todo_server | 5111 | stdlib HTTPServer | ssl.wrap_socket() |
|
||||
| transcribe_server | 8090 | FastAPI/Uvicorn | uvicorn ssl_certfile/ssl_keyfile |
|
||||
| backend_main | 8000 | FastAPI/Uvicorn | uvicorn ssl_certfile/ssl_keyfile |
|
||||
| workforce_planner | variabel | FastAPI/Uvicorn | Nicht geändert (kein __main__-Block) |
|
||||
|
||||
## Testmethode
|
||||
|
||||
### DEV-Zertifikat erstellen:
|
||||
```
|
||||
python aza_tls.py
|
||||
```
|
||||
|
||||
### Server starten:
|
||||
```
|
||||
set AZA_TLS_CERTFILE=dev-cert.pem
|
||||
set AZA_TLS_KEYFILE=dev-key.pem
|
||||
python backend_main.py
|
||||
```
|
||||
|
||||
### Testen:
|
||||
```
|
||||
curl https://localhost:8000/health --insecure
|
||||
```
|
||||
|
||||
### Fail-Start testen (ohne Zertifikat):
|
||||
```
|
||||
set AZA_TLS_REQUIRE=1
|
||||
python backend_main.py
|
||||
→ Muss mit Fehlermeldung abbrechen
|
||||
```
|
||||
|
||||
### DEV-Modus ohne TLS:
|
||||
```
|
||||
set AZA_TLS_REQUIRE=0
|
||||
python backend_main.py
|
||||
→ Startet auf HTTP mit Warnung
|
||||
```
|
||||
|
||||
## Rest-Risiken
|
||||
|
||||
1. **workforce_planner**: Hat keinen eigenen __main__-Block. TLS muss beim
|
||||
Start via uvicorn CLI konfiguriert werden:
|
||||
`uvicorn workforce_planner.api.app:app --ssl-certfile=... --ssl-keyfile=...`
|
||||
|
||||
2. **Self-Signed Zertifikat**: Browser und Clients zeigen Warnungen.
|
||||
Für Produktion muss ein CA-signiertes Zertifikat verwendet werden.
|
||||
|
||||
3. **Client-Seite**: Die Clients (basis14.py, aza_diktat_mixin.py) müssen
|
||||
ihre Server-URLs von http:// auf https:// ändern – das ist ein separater
|
||||
Schritt.
|
||||
|
||||
4. **Zertifikatsrotation**: Kein automatischer Erneuerungsmechanismus
|
||||
implementiert. Für PROD wird ACME/certbot empfohlen.
|
||||
|
||||
5. **todo_server PWA**: Service Worker erfordert HTTPS für vollständige
|
||||
Funktionalität auf iPhones – dies ist jetzt möglich.
|
||||
157
backup 24.2.26 - Kopie/security/handovers/STEP_4_1a_TLS_PROOF.md
Normal file
157
backup 24.2.26 - Kopie/security/handovers/STEP_4_1a_TLS_PROOF.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# STEP 4.1a – TLS-ENFORCEMENT VERIFIKATION (BEWEIS)
|
||||
# Status: ABGESCHLOSSEN
|
||||
# Datum: 2026-02-22
|
||||
|
||||
---
|
||||
|
||||
## 1. Listener-Tabelle
|
||||
|
||||
| Server | Port | Host | Protokoll (mit TLS) | Protokoll (ohne TLS) |
|
||||
|--------|------|------|---------------------|----------------------|
|
||||
| backend_main | 8000 (ENV PORT) | 0.0.0.0 | HTTPS only | Fail-Start (AZA_TLS_REQUIRE=1) |
|
||||
| transcribe_server | 8090 (ENV TRANSCRIBE_PORT) | 0.0.0.0 | HTTPS only | Fail-Start (AZA_TLS_REQUIRE=1) |
|
||||
| todo_server | 5111 | 0.0.0.0 | HTTPS only | Fail-Start (AZA_TLS_REQUIRE=1) |
|
||||
| workforce_planner | variabel | variabel | Über uvicorn CLI | Kein eigener __main__ |
|
||||
|
||||
---
|
||||
|
||||
## 2. Test-Kommandos und Ergebnisse
|
||||
|
||||
### TEST 1: Fail-Start ohne Zertifikat (AZA_TLS_REQUIRE=1)
|
||||
|
||||
**Kommando:**
|
||||
```
|
||||
$env:AZA_TLS_CERTFILE = ""
|
||||
$env:AZA_TLS_KEYFILE = ""
|
||||
$env:AZA_TLS_REQUIRE = "1"
|
||||
$env:MEDWORK_API_TOKEN = "test123"
|
||||
python backend_main.py
|
||||
```
|
||||
|
||||
**Ergebnis:**
|
||||
```
|
||||
FEHLER: TLS ist erforderlich (AZA_TLS_REQUIRE=1), aber
|
||||
AZA_TLS_CERTFILE und/oder AZA_TLS_KEYFILE sind nicht gesetzt.
|
||||
```
|
||||
Exit Code: 1
|
||||
|
||||
**Bewertung: PASS** – Server startet nicht ohne Zertifikat.
|
||||
|
||||
---
|
||||
|
||||
### TEST 2: HTTPS-Verbindung
|
||||
|
||||
**Kommando:**
|
||||
```python
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
r = urllib.request.urlopen('https://127.0.0.1:8444/health', context=ctx)
|
||||
```
|
||||
|
||||
**Ergebnis:**
|
||||
```
|
||||
HTTPS: 200 {"ok": true}
|
||||
```
|
||||
|
||||
**Bewertung: PASS** – HTTPS funktioniert.
|
||||
|
||||
---
|
||||
|
||||
### TEST 3: HTTP-Verbindung (muss fehlschlagen)
|
||||
|
||||
**Kommando:**
|
||||
```python
|
||||
r = urllib.request.urlopen('http://127.0.0.1:8444/health', timeout=5)
|
||||
```
|
||||
|
||||
**Ergebnis:**
|
||||
```
|
||||
http.client.RemoteDisconnected: Remote end closed connection without response
|
||||
```
|
||||
Exit Code: 1
|
||||
|
||||
**Bewertung: PASS** – HTTP wird abgelehnt. Kein HTTP-Fallback.
|
||||
|
||||
---
|
||||
|
||||
### TEST 4: TLS-Version und Cipher
|
||||
|
||||
**Kommando:**
|
||||
```python
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
with ctx.wrap_socket(socket.socket(), server_hostname='localhost') as s:
|
||||
s.connect(('127.0.0.1', 8444))
|
||||
print(s.version(), s.cipher())
|
||||
```
|
||||
|
||||
**Ergebnis:**
|
||||
```
|
||||
TLS Version: TLSv1.3
|
||||
Cipher: TLS_AES_256_GCM_SHA384
|
||||
Protocol: TLSv1.3
|
||||
Bits: 256
|
||||
```
|
||||
|
||||
**Bewertung: PASS** – TLS 1.3 mit AES-256-GCM, PFS aktiv.
|
||||
|
||||
---
|
||||
|
||||
### TEST 5: Zertifikat-Details
|
||||
|
||||
**Kommando:**
|
||||
```python
|
||||
cert = s.getpeercert(binary_form=True)
|
||||
c = x509.load_der_x509_certificate(cert)
|
||||
```
|
||||
|
||||
**Ergebnis:**
|
||||
```
|
||||
Subject: CN=localhost,O=AZA MedWork DEV
|
||||
Issuer: CN=localhost,O=AZA MedWork DEV
|
||||
Key: 4096 bit RSA
|
||||
Algo: SHA-256
|
||||
Valid: 2026-02-22 -> 2027-02-22
|
||||
```
|
||||
|
||||
**Bewertung: PASS** – RSA 4096-bit, SHA-256, Self-Signed (DEV).
|
||||
|
||||
---
|
||||
|
||||
### TEST 6: ENV-Hardcoding-Prüfung
|
||||
|
||||
**Kommando:** Grep nach "dev-cert.pem" / "dev-key.pem" in *.py
|
||||
|
||||
**Ergebnis:**
|
||||
Nur in `aza_tls.py` Zeilen 114-115 (Generierungsfunktion) und Docstring.
|
||||
NICHT in todo_server.py, transcribe_server.py, backend_main.py.
|
||||
Alle Server lesen Zertifikatspfade ausschliesslich aus ENV-Variablen.
|
||||
|
||||
**Bewertung: PASS** – Keine hardcodierten Zertifikatspfade.
|
||||
|
||||
---
|
||||
|
||||
## 3. Fix während Verifikation
|
||||
|
||||
| Datei | Zeile | Problem | Fix |
|
||||
|-------|-------|---------|-----|
|
||||
| aza_tls.py | 93-97 | `ssl_version: ssl.TLSVersion.TLSv1_2` inkompatibel mit uvicorn (ValueError: invalid protocol version 771) | Ersetzt durch `ssl_ciphers: _STRONG_CIPHERS` |
|
||||
|
||||
---
|
||||
|
||||
## 4. Gesamtbewertung
|
||||
|
||||
| Test | Ergebnis |
|
||||
|------|----------|
|
||||
| Fail-Start ohne Cert | **PASS** |
|
||||
| HTTPS-Verbindung | **PASS** |
|
||||
| HTTP-Ablehnung | **PASS** |
|
||||
| TLS >= 1.2 | **PASS** (TLS 1.3 verhandelt) |
|
||||
| Starke Cipher | **PASS** (AES-256-GCM) |
|
||||
| PFS | **PASS** (ECDHE/DHE only) |
|
||||
| Zertifikat RSA 4096 | **PASS** |
|
||||
| Keine hardcodierten Pfade | **PASS** |
|
||||
|
||||
**GESAMTERGEBNIS: PASS (8/8)**
|
||||
@@ -0,0 +1,99 @@
|
||||
# STEP 4.2 – SICHERES PASSWORT-HASHING
|
||||
# Status: ABGESCHLOSSEN
|
||||
|
||||
---
|
||||
|
||||
## Ziel
|
||||
|
||||
GAP #18: SHA-256 ohne Salt durch moderne KDF ersetzen.
|
||||
Transparente Migration bestehender Hashes ohne Forced Reset.
|
||||
|
||||
## Alte vs. neue Methode
|
||||
|
||||
| Parameter | ALT (unsicher) | NEU (sicher) |
|
||||
|-----------|----------------|--------------|
|
||||
| Algorithmus | SHA-256 | bcrypt |
|
||||
| Salt | Keines | Automatisch (eingebettet) |
|
||||
| Cost-Faktor | n/a | 12 Runden |
|
||||
| Hash-Format | 64 Hex-Zeichen | $2b$12$... (60 Zeichen) |
|
||||
| Brute-Force-Resistenz | Minimal (GPU-optimiert) | ~250ms pro Versuch |
|
||||
| Rainbow-Table-Schutz | Keiner | Salt schützt |
|
||||
|
||||
## Parameter
|
||||
|
||||
```
|
||||
bcrypt.gensalt(rounds=12)
|
||||
```
|
||||
|
||||
- Cost: 12 (2^12 = 4096 Iterationen)
|
||||
- Salt: 16 Bytes, automatisch generiert
|
||||
- Output: 60-Zeichen-String mit eingebettetem Salt + Params
|
||||
- Format: $2b$12$<22-char-salt><31-char-hash>
|
||||
|
||||
## Geänderte Dateien
|
||||
|
||||
| Datei | Zeile | Änderung |
|
||||
|-------|-------|----------|
|
||||
| basis14.py | 16-17 | `import bcrypt` hinzugefügt |
|
||||
| basis14.py | 1298-1300 | `_hash_password()`: SHA-256 → bcrypt |
|
||||
| basis14.py | 1302-1311 | NEU: `_verify_password()` mit Legacy-Erkennung |
|
||||
| basis14.py | 1313-1317 | NEU: `_is_legacy_hash()` |
|
||||
| basis14.py | 1355-1362 | Login: `_verify_password()` + automatischer Rehash |
|
||||
| basis14.py | 1459 | Registration: nutzt neuen `_hash_password()` (bcrypt) |
|
||||
| basis14.py | 1545 | Profil: `_verify_password()` für altes Passwort |
|
||||
| basis14.py | 1554 | Profil: `_hash_password()` für neues Passwort (bcrypt) |
|
||||
|
||||
## Migrationslogik
|
||||
|
||||
```
|
||||
Login-Versuch:
|
||||
1. _verify_password(pw, stored_hash) aufrufen
|
||||
2. Prüfe: beginnt stored_hash mit "$2b$" oder "$2a$"?
|
||||
JA → bcrypt.checkpw()
|
||||
NEIN → SHA-256 Legacy-Vergleich
|
||||
3. Bei erfolgreicher Anmeldung:
|
||||
_is_legacy_hash(stored_hash)?
|
||||
JA → Rehash mit bcrypt, Profil speichern
|
||||
NEIN → Nichts tun
|
||||
```
|
||||
|
||||
- Kein Forced Reset
|
||||
- Alter Hash wird beim nächsten Login automatisch ersetzt
|
||||
- Neuer Hash sofort bei Registration und Passwortänderung
|
||||
|
||||
## Legacy-Erkennung
|
||||
|
||||
Ein Hash ist Legacy (SHA-256) wenn:
|
||||
- Er NICHT mit "$2" beginnt (kein bcrypt-Prefix)
|
||||
- Er exakt 64 Zeichen lang ist (SHA-256 Hex-Output)
|
||||
|
||||
## Tests
|
||||
|
||||
| Test | Ergebnis |
|
||||
|------|----------|
|
||||
| Neuer bcrypt-Hash erstellen | PASS ($2b$12$...) |
|
||||
| bcrypt verify (korrektes PW) | PASS (True) |
|
||||
| bcrypt verify (falsches PW) | PASS (False) |
|
||||
| Legacy SHA-256 erkennen | PASS (is_legacy=True) |
|
||||
| bcrypt als nicht-Legacy erkennen | PASS (is_legacy=False) |
|
||||
| Legacy verify (korrektes PW) | PASS (True) |
|
||||
| Legacy verify (falsches PW) | PASS (False/rejected) |
|
||||
| Legacy rehash zu bcrypt | PASS (verify nach rehash True) |
|
||||
|
||||
## Nicht geändert
|
||||
|
||||
- workforce_planner/api/auth.py – nutzt bereits bcrypt (cost=default)
|
||||
- aza_persistence.py – importiert hashlib, nutzt es aber nicht für Passwörter
|
||||
- Backup-Kopien (basis14 - Kopie*.py) – bewusst nicht geändert
|
||||
|
||||
## Rest-Risiken
|
||||
|
||||
1. **bcrypt als Dependency**: Muss installiert sein (`pip install bcrypt`).
|
||||
Ist bereits Dependency von workforce_planner.
|
||||
|
||||
2. **Timing**: Legacy-SHA-256-Vergleich ist schneller als bcrypt.
|
||||
Ein Timing-Seitenkanalangriff könnte erkennen, ob ein Nutzer
|
||||
noch einen alten Hash hat. Risiko: minimal (lokale Desktop-App).
|
||||
|
||||
3. **workforce_planner cost**: Nutzt bcrypt mit Default-Rounds (10).
|
||||
Empfehlung: Auf 12 angleichen (separater Schritt).
|
||||
@@ -0,0 +1,194 @@
|
||||
# STEP 4.2a – HASH-MIGRATION PROOF (AUDIT-NACHWEIS)
|
||||
# Status: PASS
|
||||
|
||||
---
|
||||
|
||||
## Test-Setup
|
||||
|
||||
- **Datei:** `basis14.py` (Zeilen 1298–1316)
|
||||
- **Speicherort:** `kg_diktat_user_profile.json` (JSON, Feld `password_hash`)
|
||||
- **Testmethode:** Automatisiertes Python-Skript mit Backup/Restore des Original-Profils
|
||||
- **KDF:** bcrypt, cost=12, Salt eingebettet
|
||||
- **Legacy:** SHA-256, 64 Hex-Zeichen, kein Salt
|
||||
- **Testpasswörter:** `AuditTest2026!` (korrekt), `FalschesPasswort` (falsch)
|
||||
|
||||
---
|
||||
|
||||
## Test 1: Legacy-SHA-256-User anlegen
|
||||
|
||||
```
|
||||
Passwort: AuditTest2026!
|
||||
SHA-256 Hash: e99f0f4a91440e4a7d89ebd1db548df0eed586dd16e1b3c0e9bb6356220e0f3b
|
||||
Speicherort: kg_diktat_user_profile.json
|
||||
```
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---------|----------|
|
||||
| Profil gespeichert | PASS |
|
||||
| Hash ist Legacy (64 hex chars) | PASS |
|
||||
| Hash Länge = 64 | PASS |
|
||||
| Hash beginnt NICHT mit $2b$ | PASS |
|
||||
|
||||
---
|
||||
|
||||
## Test 2: Login mit falschem Passwort
|
||||
|
||||
```
|
||||
Passwort: FalschesPasswort
|
||||
Ergebnis: REJECTED
|
||||
```
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---------|----------|
|
||||
| Falsches PW abgelehnt | PASS |
|
||||
|
||||
---
|
||||
|
||||
## Test 3: Login mit korrektem Passwort + Rehash
|
||||
|
||||
### VORHER (Legacy SHA-256):
|
||||
```
|
||||
e99f0f4a91440e4a7d89ebd1db548df0eed586dd16e1b3c0e9bb6356220e0f3b
|
||||
```
|
||||
|
||||
### NACHHER (bcrypt):
|
||||
```
|
||||
$2b$12$mM1wNUuQJfYMZ23xJRfsJO/JVI0G4ucm9lT.BnTK/LVK8m/gw6CEK
|
||||
```
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---------|----------|
|
||||
| Korrektes PW akzeptiert | PASS |
|
||||
| Hash hat sich geändert | PASS |
|
||||
| Neuer Hash beginnt mit $2b$ | PASS |
|
||||
| Neuer Hash ist NICHT Legacy | PASS |
|
||||
|
||||
---
|
||||
|
||||
## Test 4: Login nach Rehash (bcrypt-Hash)
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---------|----------|
|
||||
| Korrektes PW nach Rehash | PASS |
|
||||
| Falsches PW nach Rehash abgelehnt | PASS |
|
||||
|
||||
---
|
||||
|
||||
## Test 5: Neue Registrierung → direkt bcrypt
|
||||
|
||||
```
|
||||
Neues Passwort: NeuerUser2026!
|
||||
bcrypt Hash: $2b$12$MdhU4H6CihlPMecPxRGgHuZ0rRJw.QXd1B0tqr4A8xK...
|
||||
```
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---------|----------|
|
||||
| Hash ist bcrypt | PASS |
|
||||
| Hash ist NICHT Legacy | PASS |
|
||||
| Verify korrekt | PASS |
|
||||
| Verify falsch abgelehnt | PASS |
|
||||
|
||||
---
|
||||
|
||||
## Test 6: Passwortänderung → bcrypt
|
||||
|
||||
```
|
||||
Neuer Hash: $2b$12$62I3tJxk09DaPmmu.6SsEeTgSp6UJReaJtSzs57SFO6...
|
||||
```
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---------|----------|
|
||||
| Hash ist bcrypt | PASS |
|
||||
| Altes PW abgelehnt | PASS |
|
||||
| Neues PW akzeptiert | PASS |
|
||||
|
||||
---
|
||||
|
||||
## Code-Verifikation
|
||||
|
||||
### _hash_password() – Zeile 1298–1301
|
||||
```python
|
||||
@staticmethod
|
||||
def _hash_password(pw: str) -> str:
|
||||
return bcrypt.hashpw(pw.encode("utf-8"), bcrypt.gensalt(rounds=12)).decode("utf-8")
|
||||
```
|
||||
→ Erzeugt bcrypt-Hash mit cost=12 und eingebettetem Salt.
|
||||
|
||||
### _verify_password() – Zeile 1303–1309
|
||||
```python
|
||||
@staticmethod
|
||||
def _verify_password(pw: str, stored_hash: str) -> bool:
|
||||
if stored_hash.startswith("$2b$") or stored_hash.startswith("$2a$"):
|
||||
return bcrypt.checkpw(pw.encode("utf-8"), stored_hash.encode("utf-8"))
|
||||
legacy = hashlib.sha256(pw.encode("utf-8")).hexdigest()
|
||||
return legacy == stored_hash
|
||||
```
|
||||
→ Erkennt bcrypt-Hashes am Prefix. Fällt auf SHA-256-Vergleich zurück.
|
||||
|
||||
### _is_legacy_hash() – Zeile 1311–1316
|
||||
```python
|
||||
@staticmethod
|
||||
def _is_legacy_hash(stored_hash: str) -> bool:
|
||||
if not stored_hash:
|
||||
return False
|
||||
return not stored_hash.startswith("$2") and len(stored_hash) == 64
|
||||
```
|
||||
→ Erkennt alte 64-Zeichen SHA-256-Hashes.
|
||||
|
||||
### Login mit Rehash – Zeile 1363–1367
|
||||
```python
|
||||
stored = self._user_profile.get("password_hash", "")
|
||||
if self._verify_password(pw, stored):
|
||||
if self._is_legacy_hash(stored):
|
||||
self._user_profile["password_hash"] = self._hash_password(pw)
|
||||
save_user_profile(self._user_profile)
|
||||
dlg.destroy()
|
||||
```
|
||||
→ Bei erfolgreichem Login: Legacy-Hash automatisch durch bcrypt ersetzt.
|
||||
|
||||
### Registrierung – Zeile 1465
|
||||
```python
|
||||
"password_hash": self._hash_password(pw),
|
||||
```
|
||||
→ Nutzt direkt bcrypt.
|
||||
|
||||
### Profiländerung (altes PW prüfen) – Zeile 1545
|
||||
```python
|
||||
if old_hash and not self._verify_password(pw_old_e.get(), old_hash):
|
||||
```
|
||||
→ Verifiziert mit _verify_password (bcrypt + Legacy-kompatibel).
|
||||
|
||||
### Profiländerung (neues PW setzen) – Zeile 1554
|
||||
```python
|
||||
pw_hash = self._hash_password(new_pw)
|
||||
```
|
||||
→ Nutzt direkt bcrypt.
|
||||
|
||||
---
|
||||
|
||||
## Gesamtergebnis
|
||||
|
||||
| Nr | Testfall | Ergebnis |
|
||||
|----|----------|----------|
|
||||
| 1 | Legacy-Hash anlegen & erkennen | PASS |
|
||||
| 2 | Falsches PW → abgelehnt | PASS |
|
||||
| 3 | Korrektes PW → akzeptiert + Rehash | PASS |
|
||||
| 4 | Login nach Rehash funktioniert | PASS |
|
||||
| 5 | Registrierung schreibt bcrypt | PASS |
|
||||
| 6 | Passwortänderung schreibt bcrypt | PASS |
|
||||
|
||||
**18/18 Einzelprüfungen: PASS**
|
||||
**0 Fixes notwendig**
|
||||
**Original-Profil wiederhergestellt**
|
||||
|
||||
---
|
||||
|
||||
## Bewertung: PASS
|
||||
|
||||
Alle Anforderungen erfüllt:
|
||||
- Legacy SHA-256 wird erkannt
|
||||
- Login mit korrektem/falschem PW korrekt
|
||||
- Rehash passiert automatisch beim Login
|
||||
- Nach Rehash ist der gespeicherte Hash bcrypt
|
||||
- Neue Registrierungen und Passwortänderungen verwenden direkt bcrypt
|
||||
- Kein Forced Reset notwendig
|
||||
162
backup 24.2.26 - Kopie/security/handovers/STEP_4_3_SECRET_KEY.md
Normal file
162
backup 24.2.26 - Kopie/security/handovers/STEP_4_3_SECRET_KEY.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 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 |
|
||||
@@ -0,0 +1,166 @@
|
||||
# STEP 4.4 – KLARTEXT-CREDENTIALS ENTFERNT (LÜCKE #28)
|
||||
# Status: ABGESCHLOSSEN
|
||||
|
||||
---
|
||||
|
||||
## Fundstellen
|
||||
|
||||
| Datei | Problem |
|
||||
|-------|---------|
|
||||
| `aza_email_config.json` | Klartext-Passwort im `password`-Feld: `"password": "Asdfasdf22@@As96k324"` |
|
||||
|
||||
Keine weiteren Klartext-Credentials in JSON/YAML/INI-Dateien gefunden.
|
||||
|
||||
**Hinweis:** Das hier entfernte Passwort war ein echtes Produktivpasswort.
|
||||
Es sollte umgehend beim Mail-Provider geändert werden.
|
||||
|
||||
---
|
||||
|
||||
## Neue ENV-Variablen
|
||||
|
||||
| Variable | Beschreibung |
|
||||
|----------|-------------|
|
||||
| `AZA_EMAIL_PASSWORD_0` | Passwort für das erste E-Mail-Konto |
|
||||
| `AZA_EMAIL_PASSWORD_1` | Passwort für das zweite E-Mail-Konto (falls vorhanden) |
|
||||
| `AZA_EMAIL_PASSWORD_N` | Passwort für das N-te Konto (0-basiert) |
|
||||
|
||||
### Setzen unter Windows:
|
||||
```cmd
|
||||
set AZA_EMAIL_PASSWORD_0=MeinSicheresPasswort
|
||||
```
|
||||
|
||||
### Setzen unter Linux/Mac:
|
||||
```bash
|
||||
export AZA_EMAIL_PASSWORD_0="MeinSicheresPasswort"
|
||||
```
|
||||
|
||||
### Dauerhaft (Windows PowerShell):
|
||||
```powershell
|
||||
[Environment]::SetEnvironmentVariable("AZA_EMAIL_PASSWORD_0", "MeinPasswort", "User")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Geänderte Dateien
|
||||
|
||||
| Datei | Änderung |
|
||||
|-------|----------|
|
||||
| `aza_email.py` | Neue Funktionen: `_get_account_password()`, `_strip_passwords()`, `_check_plaintext_migration()` |
|
||||
| `aza_email.py` | `load_email_config()`: Lädt Passwörter aus ENV, ignoriert Klartext in JSON, gibt Warnung |
|
||||
| `aza_email.py` | `save_email_config()`: Strippt `password` und `_password` vor dem Schreiben |
|
||||
| `aza_email.py` | Alle IMAP/SMTP-Stellen: `acc.get("password")` → `acc.get("_password")` |
|
||||
| `aza_email.py` | Account-Dialog: Passwort wird als `_password` (RAM-only) gespeichert |
|
||||
| `aza_email_config.json` | Klartext-Passwort entfernt |
|
||||
|
||||
---
|
||||
|
||||
## Architektur
|
||||
|
||||
### Passwort-Fluss (NEU):
|
||||
|
||||
```
|
||||
ENV (AZA_EMAIL_PASSWORD_0)
|
||||
│
|
||||
▼
|
||||
load_email_config() ──► acc["_password"] (nur RAM)
|
||||
│
|
||||
▼
|
||||
IMAP/SMTP Login ──► acc.get("_password", "")
|
||||
│
|
||||
▼
|
||||
save_email_config() ──► _strip_passwords() ──► JSON OHNE Passwort
|
||||
```
|
||||
|
||||
### Passwort-Fluss (GUI-Eingabe):
|
||||
|
||||
```
|
||||
Account-Dialog (Passwort-Feld)
|
||||
│
|
||||
▼
|
||||
save_account() ──► acc["_password"] = pw_input (nur RAM)
|
||||
│
|
||||
▼
|
||||
_save_config() ──► save_email_config() ──► _strip_passwords() ──► JSON OHNE Passwort
|
||||
```
|
||||
|
||||
Das `_password`-Feld existiert NUR im RAM. Es wird NIE in JSON geschrieben.
|
||||
Das `password`-Feld wird beim Laden aus JSON ignoriert und beim Speichern entfernt.
|
||||
|
||||
---
|
||||
|
||||
## Migrationsverhalten
|
||||
|
||||
Wenn `aza_email_config.json` noch ein `password`-Feld enthält:
|
||||
|
||||
1. **Warnung auf stderr:**
|
||||
```
|
||||
SICHERHEITSWARNUNG: Klartext-Passwoerter in aza_email_config.json gefunden!
|
||||
Betroffene Konten: andre.surovy@haut-winterthur.ch
|
||||
Bitte entfernen und stattdessen ENV-Variablen setzen:
|
||||
AZA_EMAIL_PASSWORD_0
|
||||
Die Passwoerter in der Datei werden IGNORIERT.
|
||||
```
|
||||
|
||||
2. **Klartext-Passwort wird NICHT übernommen** (kein Auto-Migrate)
|
||||
3. **Benutzer muss ENV-Variable setzen**, damit E-Mail funktioniert
|
||||
4. **Beim nächsten Speichern** wird das `password`-Feld automatisch entfernt
|
||||
|
||||
---
|
||||
|
||||
## Fail-Start Verhalten
|
||||
|
||||
| Szenario | Ergebnis |
|
||||
|----------|----------|
|
||||
| `AZA_EMAIL_PASSWORD_0` gesetzt | E-Mail funktioniert |
|
||||
| `AZA_EMAIL_PASSWORD_0` nicht gesetzt | E-Mail-Abruf zeigt: "Konto-Daten unvollständig" |
|
||||
| Klartext-PW in JSON | Warnung + wird ignoriert |
|
||||
| GUI-Passworteingabe | Funktioniert für Sitzung (nicht persistent) |
|
||||
|
||||
---
|
||||
|
||||
## Testergebnis
|
||||
|
||||
```
|
||||
12/12 PASS, 0/12 FAIL
|
||||
GESAMTBEWERTUNG: PASS
|
||||
```
|
||||
|
||||
| Test | Ergebnis |
|
||||
|------|----------|
|
||||
| Kein Passwort in JSON | PASS |
|
||||
| Warnung auf stderr bei Klartext | PASS |
|
||||
| Passwort NICHT in geladenen Daten | PASS |
|
||||
| ENV-PW in _password Feld | PASS |
|
||||
| Kein password Feld nach Laden | PASS |
|
||||
| Kein password in gespeicherter Datei | PASS |
|
||||
| Kein _password in gespeicherter Datei | PASS |
|
||||
| password entfernt (Konto 1) | PASS |
|
||||
| password entfernt (Konto 2) | PASS |
|
||||
| _password entfernt (nie auf Disk) | PASS |
|
||||
| Andere Felder erhalten | PASS |
|
||||
| Kein print mit password-Wert | PASS |
|
||||
|
||||
---
|
||||
|
||||
## Rest-Risiken
|
||||
|
||||
1. **Passwort im RAM:** Während der Sitzung liegt das Passwort im RAM
|
||||
(Python-String). Für eine Desktop-App akzeptabel.
|
||||
|
||||
2. **ENV-Variable sichtbar:** `AZA_EMAIL_PASSWORD_0` kann in Prozesslisten
|
||||
sichtbar sein. Empfehlung für Zukunft: OS-Keychain-Integration
|
||||
(Windows Credential Manager / macOS Keychain).
|
||||
|
||||
3. **Altes Passwort exponiert:** Das Passwort `Asdfasdf22@@As96k324` war
|
||||
in der JSON-Datei gespeichert. Es sollte beim Provider geändert werden.
|
||||
|
||||
4. **Backup-Dateien:** Die Kopie-Dateien (`aza_email - Kopie.py`, etc.)
|
||||
enthalten noch den alten Code. Diese sind Backups und nicht aktiv.
|
||||
|
||||
---
|
||||
|
||||
## Repo-Hygiene
|
||||
|
||||
- Kein Git-Repository vorhanden → kein `.gitignore`-Eintrag nötig
|
||||
- Kein History-Rewrite nötig
|
||||
- `aza_email_config.json` wurde direkt bereinigt (Passwort entfernt)
|
||||
216
backup 24.2.26 - Kopie/security/handovers/STEP_6_2FA.md
Normal file
216
backup 24.2.26 - Kopie/security/handovers/STEP_6_2FA.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# 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.
|
||||
59
backup 24.2.26 - Kopie/security/handovers/STEP_8_LEGAL.md
Normal file
59
backup 24.2.26 - Kopie/security/handovers/STEP_8_LEGAL.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# STEP 8 – Datenschutzerklärung & KI-Einwilligung
|
||||
|
||||
## Ziel
|
||||
Erstellung rechtlich belastbarer Texte für DSG (Schweiz), DSGVO (EU),
|
||||
medizinische Daten und KI-Verarbeitung.
|
||||
|
||||
## Erstellte Dateien
|
||||
|
||||
### /legal/privacy_policy.md
|
||||
Vollständige Datenschutzerklärung mit:
|
||||
- Verantwortlicher (Platzhalter für Praxisdaten)
|
||||
- 4 Verarbeitungszwecke (Dokumentation, Kommunikation, Organisation, KI)
|
||||
- Datenarten inkl. expliziter Nennung von Gesundheitsdaten (Art. 5 DSG)
|
||||
- Rechtsgrundlagen DSG + DSGVO
|
||||
- Speicherorte (lokal + Supabase + OpenAI API)
|
||||
- Auftragsverarbeiter: OpenAI (USA, SCCs), Supabase (EU-Hosting, USA-Sitz)
|
||||
- Drittlandübertragung dokumentiert
|
||||
- Betroffenenrechte DSG + DSGVO
|
||||
- Beschwerderecht (EDÖB + EU-Aufsicht)
|
||||
- Kontaktstelle
|
||||
|
||||
### /legal/ai_consent.md
|
||||
KI-Einwilligungserklärung mit:
|
||||
- Erklärung aller 4 KI-Einsatzgebiete (Transkription, KG, Interaktion, Textprüfung)
|
||||
- Konkrete Angabe der übermittelten Daten pro Funktion
|
||||
- Benennung des Anbieters (OpenAI) und der Modelle
|
||||
- Drittlandhinweis (USA, SCCs)
|
||||
- Klare Abgrenzung: Was KI NICHT entscheidet (keine Diagnosen, keine Therapie)
|
||||
- Ärztliche Verantwortung explizit festgehalten
|
||||
- Widerrufsrecht mit konkreten Folgen
|
||||
- Dokumentationspflicht
|
||||
- Unterschriftenfelder (Einwilligung + Widerruf)
|
||||
|
||||
## Betroffene Dateien
|
||||
| Datei | Aktion |
|
||||
|-------|--------|
|
||||
| /legal/privacy_policy.md | NEU erstellt |
|
||||
| /legal/ai_consent.md | NEU erstellt |
|
||||
|
||||
## Datengrundlage
|
||||
Alle Aussagen basieren auf dem tatsächlichen Code-Stand:
|
||||
- OpenAI API-Nutzung: Transkription (gpt-4o-mini-transcribe), Text (gpt-5.2/5-mini/5-nano)
|
||||
- Supabase Cloud-Sync (eu-central-1)
|
||||
- Lokale Datenspeicherung (JSON, SQLite)
|
||||
- Implementierte Sicherheitsmassnahmen aus STEP 4.x und 6
|
||||
|
||||
## Offene Punkte
|
||||
1. **Verantwortlicher:** Platzhalter – muss mit echten Praxisdaten ausgefüllt werden
|
||||
2. **Juristische Prüfung:** Beide Dokumente müssen vor Produktiveinsatz von
|
||||
einer rechtskundigen Person geprüft werden
|
||||
3. **AV-Vertrag OpenAI:** DPA/AVV mit OpenAI muss geprüft/abgeschlossen werden
|
||||
4. **AV-Vertrag Supabase:** DPA/AVV mit Supabase muss geprüft/abgeschlossen werden
|
||||
5. **Einwilligungsformular:** Muss in druckbare Form gebracht werden (PDF/Papier)
|
||||
6. **Kantonale Besonderheiten:** Aufbewahrungsfristen variieren je nach Kanton
|
||||
|
||||
## Risiken
|
||||
- Ohne juristische Prüfung sind die Texte nicht rechtsverbindlich
|
||||
- AV-Verträge mit OpenAI und Supabase fehlen noch (RED im Audit)
|
||||
- Drittlandübertragung USA ist datenschutzrechtlich sensibel
|
||||
@@ -0,0 +1,233 @@
|
||||
# STEP 9 – Backup, Recovery & Löschkonzept
|
||||
|
||||
## Ziel
|
||||
Sicherstellung von Datenverlustminimierung, Wiederherstellbarkeit
|
||||
und Erfüllung der DSG-Löschpflichten.
|
||||
|
||||
---
|
||||
|
||||
## 1. Datenübersicht
|
||||
|
||||
### 1.1 Medizinische / Personenbezogene Daten
|
||||
|
||||
| Datei / Speicher | Format | Inhalt | Sensibilität |
|
||||
|---|---|---|---|
|
||||
| `kg_diktat_user_profile.json` | JSON | Arztname, Fachgebiet, Passwort-Hash, TOTP-Secret | HOCH |
|
||||
| `kg_diktat_ablage/KG/` | Dateien | Krankengeschichten | HOCH |
|
||||
| `kg_diktat_ablage/Briefe/` | Dateien | Arztbriefe | HOCH |
|
||||
| `kg_diktat_ablage/Rezepte/` | Dateien | Rezepte | HOCH |
|
||||
| `kg_diktat_ablage/Kostengutsprachen/` | Dateien | Kostengutsprachen | HOCH |
|
||||
| `kg_diktat_ablage/Diktat/` | Dateien | Diktattexte | HOCH |
|
||||
| `kg_diktat_notes.json` | JSON | Notizen (ev. Patientenbezug) | MITTEL |
|
||||
| `kg_diktat_todos.json` | JSON | Aufgaben (ev. Patientenbezug) | MITTEL |
|
||||
| `kg_diktat_todo_inbox.json` | JSON | Eingehende Aufgaben | MITTEL |
|
||||
| `kg_diktat_medwork_contacts.json` | JSON | Praxiskontakte | MITTEL |
|
||||
| `aza_email_contacts.json` | JSON | E-Mail-Kontakte | MITTEL |
|
||||
| `aza_medwork_messages.json` | JSON | Nachrichten | MITTEL |
|
||||
| `workforce_planner.db` | SQLite | Mitarbeiter, Abwesenheiten, Audit-Log | MITTEL |
|
||||
|
||||
### 1.2 Konfigurationsdaten (nicht personenbezogen)
|
||||
|
||||
| Datei | Inhalt |
|
||||
|---|---|
|
||||
| `kg_diktat_config.txt` | Grundeinstellungen |
|
||||
| `kg_diktat_signature.txt` | Arzt-Signatur |
|
||||
| `kg_diktat_korrekturen.json` | Auto-Korrekturen |
|
||||
| `kg_diktat_textbloecke.json` | Textbausteine |
|
||||
| `kg_diktat_autotext.json` | Autotext-Vorlagen |
|
||||
| `kg_diktat_soap_presets.json` | SOAP-Profile |
|
||||
| `kg_diktat_brief_presets.json` | Brief-Profile |
|
||||
| `aza_email_config.json` | E-Mail-Konfiguration (ohne Passwort) |
|
||||
| Diverse `*_window.txt` | Fensterpositionen / UI-State |
|
||||
|
||||
### 1.3 Cloud-Daten
|
||||
|
||||
| Dienst | Daten | Speicherort |
|
||||
|---|---|---|
|
||||
| Supabase | Synchronisierte Praxisdaten | AWS eu-central-1 |
|
||||
| OpenAI API | Keine persistente Speicherung (API-Modus, max. 30 Tage) | USA |
|
||||
|
||||
---
|
||||
|
||||
## 2. Backup-Konzept
|
||||
|
||||
### 2.1 Implementierung
|
||||
|
||||
Neues Skript: **`aza_backup.py`**
|
||||
|
||||
```
|
||||
python aza_backup.py backup # Backup erstellen
|
||||
python aza_backup.py list # Backups auflisten
|
||||
python aza_backup.py verify <f> # Integrität prüfen
|
||||
python aza_backup.py restore <f> # Wiederherstellen
|
||||
python aza_backup.py cleanup # Alte Backups entfernen
|
||||
```
|
||||
|
||||
### 2.2 Was wird gesichert
|
||||
|
||||
- Alle medizinischen JSON-Dateien (17 Dateien)
|
||||
- Alle Konfigurationsdateien (12 Dateien)
|
||||
- Alle UI-State-Dateien (11 Dateien)
|
||||
- Komplettes Ablage-Verzeichnis (KG, Briefe, Rezepte, etc.)
|
||||
- Lernmodus-Export
|
||||
- Workforce-Planner-Datenbank (SQLite)
|
||||
|
||||
### 2.3 Backup-Parameter
|
||||
|
||||
| Parameter | Wert | Konfiguration |
|
||||
|---|---|---|
|
||||
| Format | ZIP (Deflate, Level 9) | Fest |
|
||||
| Intervall | Täglich empfohlen | Manuell oder Cron/Task Scheduler |
|
||||
| Aufbewahrung | 90 Tage (Standard) | `AZA_BACKUP_KEEP_DAYS` |
|
||||
| Zielverzeichnis | `./backups/` | `AZA_BACKUP_DIR` |
|
||||
| Integrität | SHA-256 pro Datei + Manifest | Automatisch |
|
||||
| Benennung | `aza_backup_YYYY-MM-DD_HH-MM-SS.zip` | Automatisch |
|
||||
|
||||
### 2.4 Verschlüsselung
|
||||
|
||||
- **Transport:** ZIP-Dateien können auf verschlüsseltem Medium gespeichert werden
|
||||
- **Empfehlung:** Backup-Zielverzeichnis auf BitLocker-verschlüsseltem
|
||||
Laufwerk oder verschlüsseltem NAS ablegen
|
||||
- **ZIP-eigene Verschlüsselung:** Nicht implementiert (Python-zipfile
|
||||
unterstützt kein starkes AES; Laufwerksverschlüsselung wird empfohlen)
|
||||
|
||||
### 2.5 Offsite-Kopie
|
||||
|
||||
- Backup-Verzeichnis (`AZA_BACKUP_DIR`) kann auf externes Medium
|
||||
oder Netzlaufwerk zeigen
|
||||
- Empfohlen: Regelmässige Kopie auf externes verschlüsseltes Medium
|
||||
- Keine automatische Cloud-Sicherung implementiert
|
||||
|
||||
### 2.6 Automatisierung (Windows Task Scheduler)
|
||||
|
||||
```
|
||||
schtasks /create /tn "AZA_Backup" /tr "python C:\...\aza_backup.py backup" /sc daily /st 22:00
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Recovery-Konzept
|
||||
|
||||
### 3.1 Recovery Point Objective (RPO)
|
||||
|
||||
| Szenario | RPO |
|
||||
|---|---|
|
||||
| Tägliches Backup | Max. 24 Stunden Datenverlust |
|
||||
| Stündliches Backup (optional) | Max. 1 Stunde |
|
||||
|
||||
### 3.2 Recovery Time Objective (RTO)
|
||||
|
||||
| Szenario | RTO |
|
||||
|---|---|
|
||||
| Einzelne Datei | < 5 Minuten |
|
||||
| Komplettes System | < 30 Minuten |
|
||||
|
||||
### 3.3 Restore-Anleitung
|
||||
|
||||
#### Schritt 1: Verfügbare Backups anzeigen
|
||||
```
|
||||
python aza_backup.py list
|
||||
```
|
||||
|
||||
#### Schritt 2: Backup-Integrität prüfen
|
||||
```
|
||||
python aza_backup.py verify backups/aza_backup_YYYY-MM-DD_HH-MM-SS.zip
|
||||
```
|
||||
|
||||
#### Schritt 3: Dry-Run (was wird wiederhergestellt?)
|
||||
```
|
||||
python aza_backup.py restore backups/aza_backup_YYYY-MM-DD_HH-MM-SS.zip --dry-run
|
||||
```
|
||||
|
||||
#### Schritt 4: Tatsächliche Wiederherstellung
|
||||
```
|
||||
python aza_backup.py restore backups/aza_backup_YYYY-MM-DD_HH-MM-SS.zip
|
||||
```
|
||||
|
||||
**Sicherheitsmechanismus:** Vor dem Restore wird automatisch eine
|
||||
Pre-Restore-Sicherung der aktuellen Daten erstellt (in `backups/.pre_restore_*/`),
|
||||
sodass der Restore rückgängig gemacht werden kann.
|
||||
|
||||
### 3.4 Test-Ergebnisse (22.02.2026)
|
||||
|
||||
| Test | Ergebnis |
|
||||
|---|---|
|
||||
| Backup erstellen | PASS – 44 Dateien, 0.06 MB |
|
||||
| Backup verifizieren (SHA-256) | PASS – alle Checksummen korrekt |
|
||||
| Restore Dry-Run | PASS – 44 Dateien korrekt aufgelistet |
|
||||
| Backup auflisten | PASS – Datum, Grösse, Name korrekt |
|
||||
| Löschung Dry-Run | PASS – Warnung zu Backups korrekt |
|
||||
|
||||
---
|
||||
|
||||
## 4. Löschkonzept
|
||||
|
||||
### 4.1 Löschfristen
|
||||
|
||||
| Datenkategorie | Aufbewahrungsfrist | Rechtsgrundlage |
|
||||
|---|---|---|
|
||||
| Krankengeschichten | 10–20 Jahre (kantonal) | Kantonale Gesundheitsgesetze |
|
||||
| Arztbriefe | 10–20 Jahre | Kantonale Gesundheitsgesetze |
|
||||
| Rezepte | 10 Jahre | OR Art. 958f |
|
||||
| Mitarbeiterdaten | Anstellungsdauer + 5 Jahre | OR Art. 128 |
|
||||
| E-Mail-Kontakte | Bis Löschung durch Benutzer | DSG Art. 6 |
|
||||
| Backups | 90 Tage (konfigurierbar) | Intern |
|
||||
|
||||
### 4.2 Technische Umsetzung
|
||||
|
||||
#### Automatische Backup-Bereinigung
|
||||
```
|
||||
python aza_backup.py cleanup
|
||||
```
|
||||
Entfernt Backups älter als `AZA_BACKUP_KEEP_DAYS` (Standard: 90 Tage).
|
||||
|
||||
#### Patientendaten löschen (Recht auf Vergessenwerden)
|
||||
```
|
||||
# Dry-Run (nur prüfen, was gelöscht wird)
|
||||
python aza_backup.py delete-patient "Nachname Vorname"
|
||||
|
||||
# Tatsächliche Löschung
|
||||
python aza_backup.py delete-patient "Nachname Vorname" --execute
|
||||
```
|
||||
|
||||
### 4.3 Lösch-Workflow
|
||||
|
||||
1. **Antrag:** Patient stellt Löschanfrage (schriftlich empfohlen)
|
||||
2. **Prüfung:** Arzt prüft, ob gesetzliche Aufbewahrungspflicht besteht
|
||||
3. **Dry-Run:** `delete-patient --dry-run` zeigt betroffene Dateien
|
||||
4. **Dokumentation:** Löschanfrage und Ergebnis im Audit-Log festhalten
|
||||
5. **Ausführung:** `delete-patient --execute`
|
||||
6. **Backups:** Bestehende Backups enthalten noch Daten – nach Ablauf
|
||||
der Aufbewahrungsfrist (90 Tage) werden diese automatisch entfernt
|
||||
7. **Cloud:** Supabase-Daten manuell löschen (aktuell kein API-Endpunkt)
|
||||
|
||||
### 4.4 Einschränkungen
|
||||
|
||||
- Löschung in bestehenden Backups nur durch Warten auf Ablauf
|
||||
oder manuelles Löschen der betroffenen Backups
|
||||
- Supabase-Cloud-Daten müssen manuell gelöscht werden
|
||||
- OpenAI-API-Daten werden gemäss OpenAI nach max. 30 Tagen gelöscht
|
||||
- Namensbasierte Suche – funktioniert nur bei Dateinamen / JSON-Inhalt
|
||||
mit Patientenname
|
||||
|
||||
---
|
||||
|
||||
## 5. Geänderte / Neue Dateien
|
||||
|
||||
| Datei | Aktion |
|
||||
|---|---|
|
||||
| `aza_backup.py` | NEU – Backup, Verify, Restore, Cleanup, Löschung |
|
||||
| `backups/` | NEU – Backup-Verzeichnis (automatisch erstellt) |
|
||||
|
||||
---
|
||||
|
||||
## 6. Risiken
|
||||
|
||||
| Risiko | Bewertung | Massnahme |
|
||||
|---|---|---|
|
||||
| Kein automatischer Backup-Schedule | MITTEL | Task Scheduler einrichten |
|
||||
| ZIP nicht AES-verschlüsselt | MITTEL | Laufwerksverschlüsselung nutzen |
|
||||
| Keine Offsite-Kopie konfiguriert | HOCH | `AZA_BACKUP_DIR` auf NAS setzen |
|
||||
| Supabase-Löschung manuell | MITTEL | API-Endpunkt implementieren |
|
||||
| Namensbasierte Löschung unvollständig | NIEDRIG | Erweiterte Suche implementieren |
|
||||
| Kein inkrementelles Backup | NIEDRIG | Bei Datenwachstum nachrüsten |
|
||||
@@ -0,0 +1,89 @@
|
||||
# HIN Technische Referenz – Quellensammlung
|
||||
|
||||
## Status: REFERENZDATEN VORHANDEN
|
||||
|
||||
Offizielle PDFs sind nicht frei downloadbar. Stattdessen wurden alle
|
||||
öffentlich verfügbaren technischen Informationen von HIN-Quellen extrahiert
|
||||
und in folgenden Dateien zusammengefasst:
|
||||
|
||||
- hin_architektur.md → Vollständige technische Referenz (Architektur, PKI, VPN, Auth, Secure Mail)
|
||||
- hin_quellen.md → Quellenverzeichnis mit allen Original-URLs
|
||||
|
||||
---
|
||||
|
||||
## 1. Öffentlich verfügbare technische Quellen
|
||||
|
||||
### Sicherheitsarchitektur / Netzwerk / SSHN
|
||||
- **SSHN (Secure Swiss Health Network):** https://support.hin.ch/de/thema/sshn.cfm
|
||||
- SCION-Technologie, redundante Transportwege, DDoS-Schutz
|
||||
- Any-to-any Verschlüsselung, zertifikatsbasierte Auth (72h Gültigkeit)
|
||||
|
||||
### PKI / Zertifikate
|
||||
- **HIN Sign (eSigning-Standard):** https://certifaction.com/hin-sign-esigning-standard-for-swiss-healthcare-sector/
|
||||
- PKI-basierte Zertifikatsverwaltung
|
||||
- HIN eID-basiertes SSO und Signatur
|
||||
|
||||
### VPN / Access Gateway
|
||||
- **HIN AGW Benutzerhandbuch (v3.1.65):** https://cdn.hin.ch/agw/manual/DE/
|
||||
- Konfiguration, SSL, Cluster, Monitoring, Administration
|
||||
- **HIN Gateway (Stargate):** https://support.hin.ch/de/service/hin-gateway.cfm
|
||||
- Cloud-native Microservice-Architektur, RESTful-APIs
|
||||
- Self-Sovereign Identity (SSI), Data Mesh
|
||||
|
||||
### Authentifizierung
|
||||
- **HIN Identität / eID:** https://www.hin.ch/de/services/hin-identitaet.cfm
|
||||
- Elektronische Identitäten mit Zwei-Faktor-Authentisierung
|
||||
- Single Sign-on für EPD und geschützte Anwendungen
|
||||
|
||||
### Secure Mail
|
||||
- **Was ist Secure Mail:** https://support.hin.ch/de/service/hin-mail-und-mobile/was-ist-secure-mail.cfm
|
||||
- S/MIME-Verschlüsselung, State-of-the-Art-Standards
|
||||
- **HIN Mail Global:** https://support.hin.ch/de/thema/hin-mail-global-en/
|
||||
- Verschlüsselter Versand an externe Empfänger
|
||||
- SMS-Code-Verifizierung, 30 Tage Link-Gültigkeit
|
||||
|
||||
### Publikationen (Geschäftsberichte, Broschüren)
|
||||
- https://www.hin.ch/de/ueber-hin/unternehmen/publikationen.cfm
|
||||
|
||||
### Externe Analyse
|
||||
- **MSXFaq – HIN Mail Analyse:** https://msxfaq.de/signcrypt/hin-mail.htm
|
||||
- Detaillierte technische Analyse der HIN Mail Architektur
|
||||
|
||||
---
|
||||
|
||||
## 2. Vorbereitete Support-Anfrage an HIN
|
||||
|
||||
**An:** support@hin.ch
|
||||
**Betreff:** Anfrage technische Sicherheits- und Architekturdokumentation
|
||||
|
||||
**Text:**
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
im Rahmen eines Sicherheits- und Compliance-Projekts im Gesundheitswesen
|
||||
benötigen wir Ihre offizielle technische Dokumentation zu folgenden Bereichen:
|
||||
|
||||
- Sicherheitsarchitektur (Gesamtübersicht)
|
||||
- PKI / Zertifikatsinfrastruktur
|
||||
- VPN / Netzwerkarchitektur (inkl. SSHN/SCION)
|
||||
- Authentifizierung (eID, 2FA, SSO)
|
||||
- Secure Mail (Verschlüsselung, S/MIME)
|
||||
|
||||
Bitte senden Sie mir die entsprechenden Dokumente (Whitepapers,
|
||||
technische Spezifikationen, Architekturdiagramme) oder teilen Sie mir mit,
|
||||
wo diese heruntergeladen werden können.
|
||||
|
||||
Vielen Dank im Voraus.
|
||||
|
||||
Mit freundlichen Grüssen,
|
||||
[Name / Organisation eintragen]
|
||||
|
||||
---
|
||||
|
||||
## 3. Erhaltene Dokumente (hier eintragen wenn vorhanden)
|
||||
|
||||
- [ ] hin_architektur.pdf
|
||||
- [ ] hin_pki.pdf
|
||||
- [ ] hin_vpn.pdf
|
||||
- [ ] hin_auth.pdf
|
||||
- [ ] hin_secure_mail.pdf
|
||||
@@ -0,0 +1,202 @@
|
||||
# HIN Sicherheitsarchitektur – Technische Referenz
|
||||
# Quelle: Öffentlich verfügbare HIN-Dokumentation (Stand Feb 2026)
|
||||
|
||||
---
|
||||
|
||||
## 1. Überblick
|
||||
|
||||
HIN (Health Info Net AG) wurde 1996 auf Initiative der FMH gegründet.
|
||||
Über 90% der Akteure des Schweizer Gesundheitswesens nutzen HIN.
|
||||
Ca. 14.500 Einzelabonnenten + 350 Institutionen.
|
||||
|
||||
Hauptprodukte:
|
||||
- HIN Mail (verschlüsselte E-Mail)
|
||||
- HIN Gateway (Stargate) – Organisationsanbindung
|
||||
- HIN Access Gateway (AGW) – VPN/Netzwerkzugang
|
||||
- HIN Identität (eID) – Authentifizierung
|
||||
- SSHN (Secure Swiss Health Network) – SCION-basiertes Netzwerk
|
||||
- HIN Sign – Digitale Signaturen
|
||||
- HIN Endpoint Security – Endgeräteschutz
|
||||
|
||||
---
|
||||
|
||||
## 2. Netzwerkarchitektur
|
||||
|
||||
### 2.1 HIN Vertrauensraum
|
||||
- Geschlossene Kommunikationsumgebung für das Gesundheitswesen
|
||||
- Governance durch: HIN, FMH, pharmaSuisse, BAG Cybersicherheit, SWITCH
|
||||
- Zugang nur für verifizierte Gesundheitsakteure
|
||||
|
||||
### 2.2 SSHN (Secure Swiss Health Network) – SCION-Technologie
|
||||
- Basiert auf SCION (ETH Zürich): "Scalability, Control and Isolation on next-generation Networks"
|
||||
- Redundante Transportwege mit automatischem Fail-over (Sekundenbruchteile)
|
||||
- Routing ausschliesslich über Schweizer Netze (kein Datenexport)
|
||||
- Schutz vor DDoS-Attacken
|
||||
- Any-to-any Verschlüsselung zwischen allen Teilnehmern
|
||||
- Zertifikatsbasierte Authentifizierung (72h Gültigkeit, Erneuerung nach 18h)
|
||||
- Erstausgestelltes Zertifikat: 7 Tage Gültigkeit
|
||||
- Core Member: Swisscom, Sunrise (ISPs mit eigenen autonomen Netzwerken)
|
||||
- Carrier: Cyberlink, VTX, Everyware (nutzen Core-Member-Netze)
|
||||
- Auch genutzt von: Swiss Secure Finance Network (SSFN) bei SIX/SNB
|
||||
- Getestet von: SIX, EDA, Armasuisse
|
||||
|
||||
### 2.3 HIN Access Gateway (AGW)
|
||||
- Virtuelle Appliance (VMware, HyperV, Qemu/Libvirt)
|
||||
- Aktuelle Version: 3.1.65
|
||||
- SHA-256 Prüfsummen für Integritätsverifikation
|
||||
- Funktionen: SSL, Cluster-Management, Monitoring, Administration
|
||||
|
||||
### 2.4 HIN Gateway (Stargate) – Neue Generation
|
||||
- Cloud-native Microservice-Architektur
|
||||
- RESTful-APIs, Open-Source-Komponenten
|
||||
- Quellcode wird quelloffen sein
|
||||
- Design orientiert an Gaia-X und European Health Data Space (EHDS)
|
||||
- Betriebsmodelle: OpenShift, Kubernetes, VMware, Microsoft, QEMU, Hybrid
|
||||
- EINE Instanz pro Organisation (nicht mandantenfähig – bewusste Sicherheitsentscheidung)
|
||||
- Transport: Wireguard-Protokoll (kein VPN-Tunneling, reiner App-zu-App-Kanal)
|
||||
- Dezentrale Identitäten (SSI), verteiltes Schlüsselmanagement
|
||||
- Programmierbare Richtlinien
|
||||
- Post-Quanten-Kryptografie vorbereitet
|
||||
|
||||
Quelle: https://support.hin.ch/de/service/hin-gateway.cfm
|
||||
|
||||
---
|
||||
|
||||
## 3. PKI / Zertifikatsinfrastruktur
|
||||
|
||||
### 3.1 HIN-eigene PKI
|
||||
- Aussteller: "HIN MGW Issuing CA 2022"
|
||||
- Zertifikatstyp: Domain-Zertifikate für S/MIME
|
||||
- CN: "Domain Certificate for <domain>"
|
||||
- SAN: "domain-confidentiality-authority@<domain>"
|
||||
- Schlüssel: RSA 4096-bit
|
||||
- Signatur: SHA256withRSAEncryption
|
||||
- Gültigkeit: 10 Jahre (Domain-Zertifikate)
|
||||
|
||||
### 3.2 SSHN-Zertifikate
|
||||
- Gültigkeit: 72 Stunden (Standard)
|
||||
- Erstausstellung: 7 Tage
|
||||
- Erneuerung: 18h nach Installation des aktuellen Zertifikats
|
||||
- CSR-Erstellung gemäss Anapaya-Dokumentation
|
||||
- Einreichung an: certificate-hvr@hin.ch
|
||||
|
||||
### 3.3 Zertifikatsverteilung
|
||||
- HIN-Backend stellt Domain-Zertifikate zentral bereit
|
||||
- Alle HIN-Gateways laden Public Keys vom HIN-Backend herunter
|
||||
- Geschlossenes System: Zertifikate nicht extern verfügbar
|
||||
- Bei Installation: Gateway fordert Zertifikat bei HIN-PKI an
|
||||
|
||||
Quelle: https://msxfaq.de/signcrypt/hin-mail.htm
|
||||
|
||||
---
|
||||
|
||||
## 4. Authentifizierung
|
||||
|
||||
### 4.1 HIN Identitätstypen
|
||||
|
||||
#### Persönliche eID
|
||||
- Vergleichbar mit digitalem Ausweisdokument
|
||||
- Identitätsprüfung via Videoidentifikation
|
||||
- Berufsqualifikation über Verbände/nationale Register verifiziert
|
||||
- Zugriff auf EPD möglich
|
||||
- Höchstes Vertrauenslevel
|
||||
|
||||
#### Persönliche eID für Organisationen
|
||||
- Organisation prüft Identität der Mitarbeitenden
|
||||
- Nur für HIN Services (nicht HIN-geschützte Anwendungen)
|
||||
- Erweiterung bestehender lokaler Identitäten (z.B. AD)
|
||||
|
||||
#### Team-Identität
|
||||
- Gemeinsame Nutzung durch mehrere Personen (z.B. MPA-Team)
|
||||
- Tieferes Vertrauenslevel
|
||||
- Kein EPD-Zugriff möglich
|
||||
- Verschlüsselung identisch zur persönlichen eID
|
||||
|
||||
### 4.2 Zwei-Faktor-Authentisierung (2FA)
|
||||
|
||||
#### HIN Client (Einzelpersonen ohne grosse IT)
|
||||
- Faktor 1: HIN Passwort
|
||||
- Faktor 2: Kryptographischer Schlüssel auf dem Gerät
|
||||
|
||||
#### HIN Gateway (Organisationen mit IT-Infrastruktur)
|
||||
- Faktor 1: SMS-Code (mTAN) oder Authentisierungs-App
|
||||
- Faktor 2: Lokaler Verzeichnisdienst (Active Directory)
|
||||
|
||||
#### Mobiler Zugriff
|
||||
- Faktor 1: SMS-Code (mTAN) oder Authentisierungs-App
|
||||
- Faktor 2: HIN Login + HIN Passwort
|
||||
|
||||
### 4.3 Single Sign-on (SSO)
|
||||
- Nach HIN-Anmeldung keine erneute Passworteingabe nötig
|
||||
- Zugriff auf HIN-geschützte Anwendungen
|
||||
- Zugriff auf EPD-Portale (nur mit persönlicher eID)
|
||||
|
||||
Quelle: https://www.hin.ch/de/services/hin-identitaet.cfm
|
||||
|
||||
---
|
||||
|
||||
## 5. Verschlüsselung / Secure Mail
|
||||
|
||||
### 5.1 HIN Mail (intern – HIN-zu-HIN)
|
||||
- S/MIME-Verschlüsselung mit Domain-Zertifikaten
|
||||
- Automatische Verschlüsselung durch Gateway/Backend
|
||||
- Gateway prüft Empfänger-Domain gegen HIN-Domainliste
|
||||
- Ausgehend: Signierung mit eigenem Zertifikat + Verschlüsselung mit Public Key des Empfängers
|
||||
- Eingehend: Signaturprüfung + Entschlüsselung mit eigenem Private Key
|
||||
- Header "X-HIN-Encrypted" markiert verschlüsselte Mails
|
||||
- Transport: SMTP über Internet (MX-Record-Auflösung)
|
||||
- TLS optional (nicht erzwungen, daher S/MIME als primärer Schutz)
|
||||
|
||||
### 5.2 HIN Mail Global (extern – an Nicht-HIN-Teilnehmer)
|
||||
- Betreff muss "(Confidential)" enthalten
|
||||
- Mail wird auf HIN-Webserver bereitgestellt
|
||||
- Empfänger erhält Benachrichtigung mit HTML-Anhang ("Secure-email.html")
|
||||
- Authentifizierung: SMS-Code an registrierte Mobilnummer
|
||||
- "Gerät merken" Option verfügbar
|
||||
- Link-Gültigkeit: 30 Tage
|
||||
- Basiert auf GINA-Verfahren von SEPPMail
|
||||
- Betreffzeile bleibt UNVERSCHLÜSSELT
|
||||
|
||||
### 5.3 Drei Betriebsmodelle
|
||||
1. **Gehostetes Postfach** (~380 CHF/Jahr): Webmail oder IMAP4, Backend verschlüsselt
|
||||
2. **HIN Gateway**: Eigener Mailserver, Gateway verschlüsselt/entschlüsselt automatisch
|
||||
3. **Webbasiert**: Zugriff via webmail.hin.ch
|
||||
|
||||
### 5.4 Technische Details Gateway
|
||||
- HIN Gateway basiert auf SEPPMail-Appliance (reduzierte Funktion)
|
||||
- Unterstützt Exchange Online Integration (O365)
|
||||
- Prüft X-OriginatorOrg und X-MS-Exchange-CrossTenant-Id
|
||||
- SMTP-Schnittstelle für interne Geräte (Relay)
|
||||
- IMAP-Zugriff über Zimbra-Plattform (unverändert)
|
||||
|
||||
Quellen:
|
||||
- https://support.hin.ch/de/service/hin-mail-und-mobile/was-ist-secure-mail.cfm
|
||||
- https://msxfaq.de/signcrypt/hin-mail.htm
|
||||
|
||||
---
|
||||
|
||||
## 6. Rechtlicher Rahmen
|
||||
|
||||
- Art. 321 StGB: Verletzung des Berufsgeheimnisses → Gefängnis oder Busse
|
||||
- Berufsgeheimnisträger: Ärzte, Apotheker, Rechtsanwälte, Revisoren, Geistliche
|
||||
- Unverschlüsselte E-Mails mit Patientendaten = Verletzung des Berufsgeheimnisses
|
||||
- HIN-Zertifizierungen entsprechen dem für EPD geforderten Vertrauenslevel
|
||||
|
||||
---
|
||||
|
||||
## 7. Zusammenfassung: HIN-Sicherheitsniveau
|
||||
|
||||
| Bereich | HIN-Standard |
|
||||
|--------------------------|--------------------------------------------------|
|
||||
| Netzwerk | SCION/SSHN, nur Schweizer Netze, DDoS-Schutz |
|
||||
| Verschlüsselung | S/MIME, RSA 4096-bit, SHA256 |
|
||||
| PKI | Eigene CA ("HIN MGW Issuing CA 2022") |
|
||||
| Zertifikate | Domain-Zertifikate, 72h SSHN-Zertifikate |
|
||||
| Authentifizierung | 2FA (mTAN/App + Passwort/AD/Geräteschlüssel) |
|
||||
| SSO | Ja, für HIN-geschützte Anwendungen |
|
||||
| Identitätsprüfung | Videoidentifikation, Berufsregister |
|
||||
| Transport | Wireguard (Stargate), SMTP+S/MIME (Legacy) |
|
||||
| Mandantentrennung | 1 Instanz pro Organisation |
|
||||
| Zukunftssicherheit | Post-Quanten-Kryptografie vorbereitet |
|
||||
| Quellcode | Open Source (Stargate) |
|
||||
| Governance | FMH, pharmaSuisse, BAG, SWITCH, HIN |
|
||||
@@ -0,0 +1,64 @@
|
||||
# HIN Quellenverzeichnis – Alle verwendeten Originalquellen
|
||||
|
||||
---
|
||||
|
||||
## Offizielle HIN-Quellen (hin.ch / support.hin.ch / cdn.hin.ch)
|
||||
|
||||
1. SSHN (Secure Swiss Health Network)
|
||||
https://support.hin.ch/de/thema/sshn.cfm
|
||||
|
||||
2. HIN Identität / eID
|
||||
https://www.hin.ch/de/services/hin-identitaet.cfm
|
||||
|
||||
3. Secure Mail
|
||||
https://support.hin.ch/de/service/hin-mail-und-mobile/was-ist-secure-mail.cfm
|
||||
|
||||
4. HIN Mail Global
|
||||
https://support.hin.ch/de/thema/hin-mail-global-en/
|
||||
|
||||
5. HIN Gateway (Stargate)
|
||||
https://support.hin.ch/de/service/hin-gateway.cfm
|
||||
|
||||
6. HIN AGW Benutzerhandbuch v3.1.65
|
||||
https://cdn.hin.ch/agw/manual/DE/
|
||||
|
||||
7. HIN Gateway Dokumentation (ZIP)
|
||||
https://download.hin.ch/gw/hin-gateway.zip
|
||||
|
||||
8. HIN Publikationen
|
||||
https://www.hin.ch/de/ueber-hin/unternehmen/publikationen.cfm
|
||||
|
||||
9. HIN Kollektivmitgliedschaft mit Gateway
|
||||
https://www.hin.ch/de/hin-mitgliedschaft/kollektivmitgliedschaft/mit-gateway.cfm
|
||||
|
||||
10. HIN Zertifizierungen
|
||||
https://www.hin.ch/de/ueber-hin/zertifizierungen/uebersicht.cfm
|
||||
|
||||
11. HIN Sign
|
||||
https://www.hin.ch/de/services/hin-sign/hin-sign.cfm
|
||||
|
||||
12. HIN Datenschutz
|
||||
https://www.hin.ch/de/ueber-hin/hin-welt/hin-und-der-datenschutz/hin-und-der-datenschutz.cfm
|
||||
|
||||
## Externe technische Analysen
|
||||
|
||||
13. MSXFaq – HIN Mail im Schweizer Gesundheitswesen (Frank Carius)
|
||||
https://msxfaq.de/signcrypt/hin-mail.htm
|
||||
→ Detaillierteste externe Analyse der HIN-Architektur
|
||||
|
||||
14. HIN Sign / Certifaction
|
||||
https://certifaction.com/hin-sign-esigning-standard-for-swiss-healthcare-sector/
|
||||
|
||||
15. VSHN – HIN Success Story
|
||||
https://www.vshn.ch/en/success-stories/hin-health-info-net/
|
||||
|
||||
## SCION-Technologie
|
||||
|
||||
16. Anapaya – CSR-Erstellung für SSHN
|
||||
https://docs.anapaya.net/en/latest/edge/user-guides/crypto-provisioning/#edge-appliance-generate-csr
|
||||
|
||||
## Kontakt HIN Support
|
||||
|
||||
- E-Mail: support@hin.ch
|
||||
- Telefon SSHN: 0848 830 740
|
||||
- Zertifikate SSHN: certificate-hvr@hin.ch
|
||||
Reference in New Issue
Block a user