fix(security): stream tickets instead of session tokens in URLs (FLAW-22)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m32s

WebSocket and EventSource put the 30-day session token into the URL,
where proxies log it. The app now gets a one-time ticket right before
connecting (POST /auth/stream-ticket, 60 s, bound to the token version);
?token= is no longer accepted for streams, non-browser clients may use
the Authorization header.

With BASE_URL set, CORS allows only that origin and WebSocket upgrades
only it or the requested host; credentials are off (no cookies exist).
Logout, ending all sessions, a new password and deactivation close all
live streams of the account; clients with a valid session reconnect
with a fresh ticket.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 14:46:58 +01:00
1 parent 5c711c2cf7
commit f57a9884d1
17 files changed
+335 -41

No files matched your search

+2 -1
View File
@@ -31,7 +31,7 @@ Schweregrade: **Hoch** (Datenabfluss oder Rechteumgehung möglich), **Mittel** (
| FLAW-19 | Qualität | Frontend-Tests nur für den Outliner, `lint` ist ein Platzhalter | Niedrig |
| FLAW-20 | Qualität | ~~Sehr große Dateien~~; duplizierte Zugriffslogik | Niedrig |
| FLAW-21 | Repo | ~~`LICENSE` fehlt, obwohl README MIT angibt~~ | **Behoben** |
| FLAW-22 | Netzwerk | Token in der URL (WS/SSE), CORS `*`, `CheckOrigin` immer `true` | Niedrig |
| FLAW-22 | Netzwerk | ~~Token in der URL (WS/SSE), CORS `*`, `CheckOrigin` immer `true`~~ | **Behoben** |
| FLAW-23 | SQLite | Single-Writer-Engpass bei vielen gleichzeitigen Schreibern | Niedrig |
| FLAW-24 | Gruppen | ~~Organisations-Endpunkte ohne Berechtigungsprüfung~~ | **Behoben** |
| FLAW-25 | Workspace | ~~Struktur-Aktionen an fremd gesperrten Bullets nur teilweise ausgeführt~~ | **Behoben** |
@@ -174,6 +174,7 @@ Schweregrade: **Hoch** (Datenabfluss oder Rechteumgehung möglich), **Mittel** (
- README und Badge nennen die MIT-Lizenz und verlinken auf `LICENSE`; die Datei existiert nicht im Repository.
### FLAW-22: Netzwerk-Details
- **Status: Behoben.** WebSocket/SSE nutzen Einmal-Tickets (60 s, einmalig, an `token_version` gebunden) statt des Sitzungs-Tokens in der URL; `?token=` wird für Streams nicht mehr angenommen. Mit `BASE_URL` erlaubt CORS nur diese Adresse und der WebSocket nur sie oder den angefragten Host; Credentials sind generell aus (es gibt keine Cookies). Logout, „Alle Sitzungen beenden“, Passwortwechsel und Deaktivierung trennen alle Streams des Kontos (`Hub.DisconnectUser`). Tests: `streamticket_test.go`, `TestLogoutEndsLiveStreams`. Ohne `BASE_URL` bleibt die Origin-Prüfung aus, weil Proxys den `Host` oft umschreiben.
- `authenticateUserFromReq` akzeptiert das Token als `?token=` (nötig für `WebSocket`/`EventSource`, die keine Header setzen). Tokens landen dadurch in Proxy- und Zugriffslogs.
- `AllowedOrigins: ["*"]` (Auth läuft über `Authorization`-Header, nicht über Cookies, das Risiko ist daher begrenzt) und `CheckOrigin` liefert immer `true`.
- Ein einmal verbundener WebSocket/SSE-Stream wird bei Logout oder Token-Widerruf nicht getrennt.