diff --git a/docs/auth-konfiguration.md b/docs/auth-konfiguration.md index 0f4c6af..5606fbe 100644 --- a/docs/auth-konfiguration.md +++ b/docs/auth-konfiguration.md @@ -170,7 +170,7 @@ Automatisiert nicht abdeckbar (Spec § 7.1); vor dem Release abhaken: - **Keine Wartezeit-Angabe:** Die 429-Antwort trägt kein `Retry-After`; die App nennt keine Dauer. - **Drosselung nur je IP**, nicht je Konto. - **Mail-Limit instanzweit** (30/h): Angreifer können Mailausfälle auslösen. Während der Sperre ist die Existenz eines Kontos über 429 vs. 200 erkennbar (R3, akzeptiert F12). -- **SMTP-Ausfall:** Auf API-Ebene unterscheiden sich bekannte und unbekannte Adressen (500 vs. 200, Spike #37). Die App zeigt neutral (F13, R16); Restrisiko rohe API akzeptiert. +- **SMTP-Ausfall:** Auf API-Ebene unterscheiden sich bekannte und unbekannte Adressen (500 vs. 200, Spike #37). Die App zeigt neutral (F13, R17); Restrisiko rohe API akzeptiert. - **Enumeration auf API-Ebene:** Signup mit unbestätigter Adresse gibt den echten Nutzer zurück, mit bestätigter einen erfundenen (R6). Die App zeigt in beiden Fällen denselben Bildschirm. - **Abgemeldete Geräte nach Reset:** Access-Tokens gelten bis zu 60 min weiter (R8, F1). - **Abgebrochene Recovery** bleibt nach einem Reload als normale Anmeldung bestehen (R12). diff --git a/docs/specs/1-auth-registrierung-anmeldung.md b/docs/specs/1-auth-registrierung-anmeldung.md index fa52159..3e35868 100644 --- a/docs/specs/1-auth-registrierung-anmeldung.md +++ b/docs/specs/1-auth-registrierung-anmeldung.md @@ -428,7 +428,7 @@ Der Backend-PR (B1–B3) wird als erster PR zu #1 gemergt. Die Merge-Reihenfolge |---|---|---|---| | R1 | Supabase-Standardwerte unterscheiden sich je nach Quelle (Binary, CLI, Docker) und Version | Tests sind grün, Produktion verhält sich anders (Krit. 7, 8) | Alle Werte pinnen (E8); B0 hat CLI 3.0.0 und Image `v2.197.0` verifiziert; Image pinnen (D1) und bei jedem GoTrue-Update B0 (c), (d), (f) wiederholen; Release-Checkliste | | R2 | **Drosselung (Krit. 7) wirkt nur mit `GOTRUE_RATE_LIMIT_HEADER`** (B0, A2). Ohne die Einstellung gibt es gar keine IP-Drosselung; auch mit ihr werden Requests **ohne** den Header nicht gedrosselt. Fehlt der Proxy oder ist GoTrue direkt erreichbar, ist die Drosselung umgehbar; hängt der Proxy den Header nur an, fälschen Clients ihre IP. Die 429-Antwort trägt kein `Retry-After`. Lokal (CLI) gibt es keine Drosselung. | Passwort-Raten ungebremst (Krit. 7), oder Umgehung durch gefälschte IPs | AUTH-CFG-13 Pflicht; D1: Proxy überschreibt den Header, GoTrue nur über den Proxy erreichbar; CI-Test gegen Extra-Container (D2, T3, F11); Release-Checkliste am echten Gateway (31 Fehllogins, gefälschter Header wird überschrieben). App-Text nennt keine Dauer. | -| R3 | `email_sent` ist **instanzweit** (B0 (d) bestätigt, 30 Mails/h) und wirkt nur über das Env, in der lokalen CLI nicht | Ab der 31. Mail pro Stunde gehen keine Mails mehr raus; Angreifer können das billig auslösen. **Seitenkanal:** Während der Sperre antwortet `/recover` für bestehende Adressen 429, für unbekannte 200 (Signup abgeleitet umgekehrt, siehe 4.3); über die rohe API wird die Existenz eines Kontos erkennbar. Nebenwirkung der Gegenmaßnahme: Nutzer erfahren in der App nicht, dass ihre Mail wegen der Sperre verzögert ist oder ausbleibt. | 30/h reicht laut Chris zum Start (F4). **F12 (Chris, 2026-11-02):** In der App verdeckt: `over_email_send_rate_limit` bei Registrierung und Reset führt zum selben neutralen CheckEmailScreen wie 200, „E-Mail erneut senden“ auf dem CheckEmailScreen bestätigt ebenso neutral (4.4); nur nach Login mit richtigem Passwort (Krit. 6) erscheint der Drosselungshinweis. Krit. 4 bleibt in der App erfüllt; der neutrale Zusatz „kann einige Minuten dauern“ steht immer da. Auf API-Ebene akzeptiert. Der Angriff erzeugt vor allem Ausfälle. Monitoring auf 429 `over_email_send_rate_limit`, Wert bei Bedarf erhöhen; Proxy-Lösung erst bei Bedarf. Nicht automatisiert getestet (lokal wirkungslos). **F13 (SMTP-Ausfall):** gleiches Muster, siehe R16. | +| R3 | `email_sent` ist **instanzweit** (B0 (d) bestätigt, 30 Mails/h) und wirkt nur über das Env, in der lokalen CLI nicht | Ab der 31. Mail pro Stunde gehen keine Mails mehr raus; Angreifer können das billig auslösen. **Seitenkanal:** Während der Sperre antwortet `/recover` für bestehende Adressen 429, für unbekannte 200 (Signup abgeleitet umgekehrt, siehe 4.3); über die rohe API wird die Existenz eines Kontos erkennbar. Nebenwirkung der Gegenmaßnahme: Nutzer erfahren in der App nicht, dass ihre Mail wegen der Sperre verzögert ist oder ausbleibt. | 30/h reicht laut Chris zum Start (F4). **F12 (Chris, 2026-11-02):** In der App verdeckt: `over_email_send_rate_limit` bei Registrierung und Reset führt zum selben neutralen CheckEmailScreen wie 200, „E-Mail erneut senden“ auf dem CheckEmailScreen bestätigt ebenso neutral (4.4); nur nach Login mit richtigem Passwort (Krit. 6) erscheint der Drosselungshinweis. Krit. 4 bleibt in der App erfüllt; der neutrale Zusatz „kann einige Minuten dauern“ steht immer da. Auf API-Ebene akzeptiert. Der Angriff erzeugt vor allem Ausfälle. Monitoring auf 429 `over_email_send_rate_limit`, Wert bei Bedarf erhöhen; Proxy-Lösung erst bei Bedarf. Nicht automatisiert getestet (lokal wirkungslos). **F13 (SMTP-Ausfall):** gleiches Muster, siehe R17. | | R4 | GoTrue hat keinen Schalter gegen Magic Link/OTP (`/otp`) | Mails außerhalb von Krit. 11, Konten ohne Passwort | Gateway-Sperre (AUTH-CFG-20, D1). B0 (A7): `/otp` legt mit gültigen Metadaten Konten an, der Trigger schützt also nicht. Lokal nicht absicherbar, deshalb nur dokumentiert | | R5 | Self-hosted GoTrue lädt Mailvorlagen per URL; bei Fehlern still auf die englische Standardvorlage | Krit. 11 verletzt, ohne dass es auffällt | Vorlagen-URL im Health-Check, Release-Checkliste: Testmail de/en auf Staging | | R6 | Enumeration auf API-Ebene: (a) Signup mit unbestätigter Adresse gibt den echten Nutzer zurück, mit bestätigter Adresse einen erfundenen (B0 bestätigt). (b) **Fehlkonfiguration:** Mit `GOTRUE_SMS_AUTOCONFIRM=true` (CLI-Standard, Self-Hosting-Vorlage `ENABLE_PHONE_AUTOCONFIRM=true`) liefert Signup mit bestätigter Adresse 422 `user_already_exists` (B0, A1). (c) Seitenkanal bei erschöpftem Mail-Limit, siehe R3. | (a) Mit Skripten lässt sich die Existenz unbestätigter Konten erkennen. (b) Existenz bestätigter Konten offen lesbar, Krit. 4 auf API-Ebene gebrochen. | (a) Supabase-Standardverhalten; die App verrät nichts (Krit. 4 bezieht sich auf das Erleben in der App). Bei Bedarf später Captcha oder eigener Proxy. (b) AUTH-CFG-24 in `config.toml` und Env; API-Regressionstest (7.1 Krit. 4); Release-Checkliste. (c) in der App verdeckt (neutraler Screen bei Registrierung, Reset und Resend auf dem CheckEmailScreen, 4.4), auf API-Ebene akzeptiert (F12, R3). | @@ -441,7 +441,7 @@ Der Backend-PR (B1–B3) wird als erster PR zu #1 gemergt. Die Merge-Reihenfolge | R13 | Drosselungstests leeren IP-Buckets für bis zu 5 min | Nachfolgende Login-Tests scheitern zufällig | Weitgehend entschärft: Drosselungstests laufen gegen den Extra-Container (D2) mit eigenen, im Speicher gehaltenen Buckets; die CLI drosselt ohnehin nicht. Im Projekt `throttle` nutzt jeder Test eine eigene zufällige `X-Forwarded-For`-IP (simuliert den Proxy, gewollt). Gegen die gemeinsame CLI-Instanz werden **keine** gefälschten `X-Forwarded-For`-Header verwendet. | | R14 | Ohne HIBP prüft nur der Client die Liste häufiger Passwörter (F2) | Über die rohe API lassen sich häufige Passwörter mit mindestens 10 Zeichen setzen | Bewusst akzeptiert; der Server erzwingt die Mindestlänge 10, die Drosselung (AUTH-CFG-10) bremst Rateversuche | | R15 | Bundle-/Application-ID ist ein Platzhalter (F8) | Nach der ersten Store-Veröffentlichung nicht mehr änderbar | Vor dem Store-Release festlegen (Release-Checkliste) | -| R16 | **SMTP-Ausfall verrät Konten auf API-Ebene** (Spike #37/#38, GoTrue v2.197.0): `/recover` bekannt 500 `unexpected_failure` / unbekannt 200; `/resend` unbestätigt 500 / unbekannt 200; `/signup` neu 500, bestätigt 200. Kein Zeit-Seitenkanal (< 60 ms). Offen: SMTP, der die Verbindung annimmt, aber nicht antwortet (Timeout). | Konten-Enumeration über die rohe API; in der App durch F13 verdeckt. Nebenwirkung: Bei echtem SMTP-Ausfall erfahren Nutzer nichts und warten auf eine Mail. | **F13 (Chris, 2026-11-03, Option 1):** Die App behandelt jeden Nicht-Eingabe-, Nicht-Transport-Fehler dieser Aufrufe wie Erfolg (4.4). **Restrisiko rohe API bleibt akzeptiert** (wie R3, R6). **Betrieb:** Alarm/Monitoring auf GoTrue-Logs `unexpected_failure`/500 bei recover/resend/signup (`docs/auth-konfiguration.md` § 5, Release-Blocker). Der neutrale Zusatz „kann einige Minuten dauern“ bleibt. | +| R17 | **SMTP-Ausfall verrät Konten auf API-Ebene** (Spike #37/#38, GoTrue v2.197.0): `/recover` bekannt 500 `unexpected_failure` / unbekannt 200; `/resend` unbestätigt 500 / unbekannt 200; `/signup` neu 500, bestätigt 200. Kein Zeit-Seitenkanal (< 60 ms). Offen: SMTP, der die Verbindung annimmt, aber nicht antwortet (Timeout). | Konten-Enumeration über die rohe API; in der App durch F13 verdeckt. Nebenwirkung: Bei echtem SMTP-Ausfall erfahren Nutzer nichts und warten auf eine Mail. | **F13 (Chris, 2026-11-03, Option 1):** Die App behandelt jeden Nicht-Eingabe-, Nicht-Transport-Fehler dieser Aufrufe wie Erfolg (4.4). **Restrisiko rohe API bleibt akzeptiert** (wie R3, R6). **Betrieb:** Alarm/Monitoring auf GoTrue-Logs `unexpected_failure`/500 bei recover/resend/signup (`docs/auth-konfiguration.md` § 5, Release-Blocker). Der neutrale Zusatz „kann einige Minuten dauern“ bleibt. | | R16 | SMTP-Anbieter und Absender-Domain sind offen (F7) | Ohne SMTP keine Mails auf Staging/Prod | Lokal Mailpit; D1 ist Release-Blocker, kein Merge-Blocker | ### 9.2 Fragen an Chris (alle entschieden am 2026-11-02) @@ -460,7 +460,7 @@ Der Backend-PR (B1–B3) wird als erster PR zu #1 gemergt. Die Merge-Reihenfolge | F10 | Fundament als eigenes Issue? | entschieden, umgesetzt | Ja: Issue #31 angelegt, #1 hängt davon ab | E1, 5.1, 8.1, 8.4 | | F11 | (Spike Q1) Krit. 7: Wo läuft der Drosselungstest, da die CLI keine IP-Drosselung kann? | entschieden (2026-11-02) | Im CI gegen einen zusätzlich gestarteten GoTrue-Container mit gesetztem `GOTRUE_RATE_LIMIT_HEADER`; zusätzlich manuelle Prüfung am echten Gateway vor dem Release | 7, 7.1 Krit. 7, D2, T3, R2, R13 | | F12 | (Spike Q2) Seitenkanal bei erschöpftem Mail-Limit (`/recover`: bestehende Adresse 429, unbekannte 200) | entschieden (2026-11-02) | In der App verdeckt: Bei `over_email_send_rate_limit` zeigen Registrierung und Passwort-Reset denselben neutralen Screen „Prüfe dein Postfach … falls …“ (Krit. 4 bleibt in der App erfüllt), mit neutralem Zusatz „kann einige Minuten dauern“. Ergänzt (Chris, 2026-11-02): Auch „E-Mail erneut senden“ auf dem CheckEmailScreen bestätigt neutral; nach Login mit richtigem Passwort (Krit. 6) bleibt der Drosselungshinweis. Über die rohe API bleibt der Kanal sichtbar: akzeptiert und als Risiko dokumentiert (Limit bleibt bei 30/h laut F4). | 4.2, 4.3, 4.4, 5.4, 7.1 Krit. 4, R3, R6 | -| F13 | (Issue #37, Spike #38) SMTP-Ausfall verrät, ob ein Konto existiert (500 vs. 200 bei `/recover`, `/resend`, `/signup`) | entschieden (2026-11-03, Chris: Option 1) | Neutrale Seite im Client: Jeder Fehler dieser Aufrufe, der nicht von der Eingabe oder vom Transport kommt und nicht die IP-Drosselung ist, zählt wie Erfolg (analog F12), auch „E-Mail erneut senden“ im LoginScreen. Das Logging bleibt über das zentrale Logger-Utility. Betriebshinweis: Monitoring auf SMTP-Fehler. | 4.4, R16, `docs/auth-konfiguration.md` § 5 | +| F13 | (Issue #37, Spike #38) SMTP-Ausfall verrät, ob ein Konto existiert (500 vs. 200 bei `/recover`, `/resend`, `/signup`) | entschieden (2026-11-03, Chris: Option 1) | Neutrale Seite im Client: Jeder Fehler dieser Aufrufe, der nicht von der Eingabe oder vom Transport kommt und nicht die IP-Drosselung ist, zählt wie Erfolg (analog F12), auch „E-Mail erneut senden“ im LoginScreen. Das Logging bleibt über das zentrale Logger-Utility. Betriebshinweis: Monitoring auf SMTP-Fehler. | 4.4, R17, `docs/auth-konfiguration.md` § 5 | Es gibt keine offenen fachlichen Fragen mehr, die die Umsetzung von #1 blockieren. F7 blockiert nur das Release. @@ -528,4 +528,4 @@ Nach der Freigabe am 2026-11-02 eingearbeitet. Die Texte der Akzeptanzkriterien 1. **Entscheidung Chris (Option 1):** Bei `recover`, `resend` und `signUp` zeigt die App für jeden Fehler, der weder Eingabe- noch Transportfehler noch IP-Drosselung ist (insbesondere HTTP 5xx / `unexpected_failure`), dieselbe neutrale Seite wie bei Erfolg. Umsetzung als Allowlist in `openEmailInputFailureFor` und `resendFailureFor`; der Mapper liefert 5xx mit Status bei diesen Aufrufen als `unexpected` (statt `network`), ein `signUp`-500 ohne bekannte Trigger-Meldung ebenfalls (statt `registrationRejected`). 2. **Login-Resend (Krit. 6):** Auch dort ist ein SMTP-Fehler neutral (Spike: `/resend` bekannt 500 / unbekannt 200). `emailSendLimited` zeigt dort weiter den Drosselungshinweis (F12). -3. **4.4, R16, F13** ergänzt; Betriebshinweis in `docs/auth-konfiguration.md`. +3. **4.4, R17, F13** ergänzt; Betriebshinweis in `docs/auth-konfiguration.md`.