E2EE-Status — Stand 2026-05-08
Dieses Dokument beschreibt den aktuellen Code-Stand und liefert eine aktualisierte Roadmap. Es ersetzt nicht den ursprünglichen E2EE-Plan, sondern konkretisiert ihn nach Audit des bestehenden Chat-Codes.
Was steht heute (Audit)
Es gibt keine Verschlüsselung in Prilog-Chat. Konkret:
- Web-Client (
prilog-web-client) hat einen self-rolled Matrix-Client:src/gateways/matrix/matrix-gateway.ts(280 Zeilen) — direkte HTTP-Calls gegen Synapsesrc/features/chat/chat-sync.ts(131 Zeilen) — eigener/sync-Long-Pollsrc/features/chat/chat-store.ts(560 Zeilen) — eigene Room/Timeline-Verwaltung- IndexedDB-Persistenz selbst geschrieben
- Keine Olm/Megolm-Codepfade. Suche nach
encryption|m.room.encrypted|olm|megolm|cryptoinprilog-web-client/src/features/chatundgateways/matrixliefert null Treffer. matrix-js-sdkist nicht inpackage.json(prilog-web-client).- Auf dem Server: Synapse läuft mit den Defaults — neue Räume werden automatisch unverschlüsselt angelegt (kein
encryption_disabled_by_default-Override und auch keinencryption_enabled_by_default_for_room_type=all).
Das ist konsistent mit dem ursprünglichen Plan (project_e2ee_plan.md), der diesen Status als Ausgangspunkt nimmt. Die 4-6-Wochen-Schätzung ist realistisch, nicht konservativ — eher knapp.
Warum E2EE keine Nacht-Aktion ist
Wenn wir matrix-js-sdk einziehen, ersetzen wir im Wesentlichen alle drei Dateien oben. Das hat zwei harte Konsequenzen:
- Halbgare Crypto ist schlimmer als keine. Eine teilweise migrierte Olm-Setup, bei der Outgoing-Megolm-Sessions nicht korrekt rotiert werden oder Device-Keys nicht persistiert werden, suggeriert Sicherheit, die nicht da ist. Datenschützer können das angreifen.
- Sync-Loop und Crypto sind verzahnt. Olm-Pre-Keys müssen vor dem ersten verschlüsselten Send verfügbar sein. Megolm-Session-Keys werden per
m.room.encryptedto-device verteilt. Das alles geht durch den Sync-Loop. Eigene Sync-Loop + matrix-js-sdk-Crypto ist möglich, aber die Verkabelung dauert genauso lang wie eine saubere Vollmigration.
3-Stufen-Plan (validiert)
Der ursprüngliche Plan hält. Hier mit konkreten Werkzeugen pro Stufe:
Stufe 0 — Foundation (kann jetzt ohne Risiko gemacht werden)
Das was wir vorbereiten können, ohne dass der Chat angefasst wird:
- Schema-Erweiterungen im Backend, die später als Aufnahme-Slots dienen:
tenant_e2ee_settings— pro Tenant: e2ee_enabled, recovery_key_strategy ('school_admin' | 'individual'), default_room_encryption ('on_creation' | 'opt_in').space_encryption_state— pro Space: encryption_pending, encryption_active, encrypted_since.device_trust— pro UserDirectoryEntry × DeviceID: trust_level ('verified_by_admin' | 'self' | 'cross_signed' | 'unknown'), verified_at, verified_by_admin_id.
- Recovery-Key-Slot im Tenant-Modell: encrypted_recovery_key_blob (zu späterem Zeitpunkt von Schul-Admin gesetzt, mit dem Tenant-Master-Recovery-Key verschlüsselt — ein zweiter Schlüssel im Hetzner Vault).
- Audit-Tabelle
e2ee_events— append-only Log für: Device-Verify, Recovery-Key-Setup, Key-Backup-Restore, Encryption-Toggle. - Doku der Bedrohungs-Modelle: was schützt E2EE vor wem?
Diese Stufe ist non-destructive — sie verändert das aktuelle Verhalten nicht, schafft nur die Aufnahme-Strukturen. Kann ich machen, ohne dass morgen irgendwas brennt. (Empfehlung: separate PR, sauber durchreviewen.)
Stufe 1 — Transport (1 Woche)
Ziel: TLS-Härte, ohne Olm/Megolm.
- HSTS-Header in nginx (sollte da sein, prüfen)
- TLS 1.3 only (sollte da sein, prüfen)
- Synapse
federation_verify_certificates: true(Default ja) - Backup von Synapse-Signing-Keys über die Backup-Pipeline (Phase 5) — wichtig damit Cross-Signing-Verlust nicht den Tenant zerstört
- Audit der CSP-Header im Web-Client, damit kein In-Browser-XSS Chat-Tokens abziehen kann
Stufe 1 hat den geringsten Sicherheits-Lift, aber sie ist Pflicht-Hygiene und kann parallel zur Stufe 0 laufen. Wir vergeben uns nichts wenn wir das jetzt machen.
Stufe 2 — Server-Side (Synapse-Konfig, 3-5 Tage)
Ziel: Neue Räume werden serverseitig encrypted angelegt, Clients sind vorbereitet aber nicht zwingend.
- Synapse
encryption_enabled_by_default_for_room_type: invite(oder all) - Test-Tenant mit
dummy-Crypto-Client (matrix-js-sdk reines Encrypt-Setup, kein Sync) — Räume werden verschlüsselt angelegt, sind aber für den Web-Client nicht lesbar (zeigt "Verschlüsselte Nachricht" statt Inhalt)
Stufe 2 ist rückwärts-kompatibel-brechend für alte Clients und macht nur Sinn wenn Stufe 3 zeitnah folgt. Sonst sind die Räume "eingefroren".
Stufe 3 — End-to-End (matrix-js-sdk, 3-4 Wochen)
Ziel: Web-Client kann verschlüsselte Räume vollständig.
Schrittfolge:
- Sync-Loop migrieren auf
matrix-js-sdkMatrixClient. ~1 Woche inkl. IndexedDB-Migration und Re-Test aller Chat-Features. - Olm-Crypto initialisieren mit
cryptoStore(IndexedDB). ~3 Tage. - Device-Trust-Flow:
- Cross-Signing-Setup beim ersten Login (Master-Key + Self-Signing-Key).
- Schul-Recovery-Key: Master-Key wird mit einem Recovery-Schlüssel verschlüsselt, der vom Tenant-Admin (Schul-Admin) gehalten wird. Bei Device-Verlust kann der Admin einen neuen Login authorisieren. Das ist die Prilog-spezifische Erweiterung, die Familien-Schulen brauchen (sonst bricht jeder Lehrer-Wechsel den Klassen-Chat).
- Auto-Vertrauen: Tenant-Admin-Devices werden vom Backend gegengesigniert (Backend hält ein Cross-Signing-Privat-Key pro Tenant). Dadurch sind alle Lehrer-Devices automatisch verifiziert ohne manuelle Emoji-Vergleichs-Zeremonie.
- Key-Backup mit Server-Side-Backup-Module (m.megolm_backup.v1): automatisches Hochladen aller Megolm-Keys, Recovery beim Login.
- UI-Indikatoren: Schloss-Icon pro Raum, Device-Liste mit Trust-Level, Recovery-Key-Setup-Wizard.
- E2E-Tests: Lehrerin schickt Nachricht, neuer Lehrer-Account bekommt sie nach Login + Recovery-Key.
Phase 0 jetzt anfangen?
Empfehlung: ja, aber als separate PR mit explizitem Review. Was Phase 0 beinhaltet:
- 4 Migrations (tenant_e2ee_settings, space_encryption_state, device_trust, e2ee_events)
- Konfig-Service
tenant-e2ee-config.service.ts(read-only Flags lesen, später schreibend) - Endpunkt
GET /admin/v1/tenant-e2ee-status(zeigt: encryption_enabled false, aber Schema bereit) - Drift-Detection-Erweiterung:
e2eeReadinessSpalte (was muss noch passieren bis Stufe 1 startbar)
Heute Nacht mache ich diese Migrations bewusst NICHT, weil:
- Sie berühren Tenant-Schema und sollten reviewt sein, bevor sie auf Production landen.
- Eine Migration die "vorbereitend" ist, ohne dass die nutzende Logik schon klar ist, kann später inkonsistent werden (Spalten-Namen ändern sich im Laufe der Implementation).
Ich habe kein Phase-0-Code-Eingriff gemacht. Diese Datei ist die Vorlage, gegen die wir gemeinsam bei der nächsten Session entscheiden können, was wir genau wann anfassen.
Was Lee zur Vorbereitung tun kann (ops, kein Code)
- Recovery-Master-Key generieren (32 random bytes hex) und in einem Hetzner-Vault hinterlegen (NICHT im Repo, NICHT in der Backend-DB im Klartext). Der wird Stufe 3 brauchen.
- CVE-Approval-Token erzeugen (passend zum CVE-Hotpath aus Phase 6), Hash in
CVE_APPROVAL_TOKEN_HASHenv eintragen, Klartext in 1Password. - Backup-Bucket bei Hetzner Object Storage anlegen — siehe Phase 5 Open-Items.
Damit ist die operative Vorbereitung an dir, der Code-Pfad an mir.