Spezifikation: Magic-Code Login
Slug magic_code_login. Vollständigkeit (Sektions-Füllgrad) über alle 13 Schritte: 60%. Achtung: Score misst nur ob Sektionen gefüllt sind, nicht fachliche Korrektheit oder Widerspruchsfreiheit. Pro Schritt werden neun Prüfebenen ausgewiesen: UI-Lokation, Eingabe-Felder, Duplikat-Prüfung, Gate-Aktionen, Code-Verknüpfung, CRM-Kategorisierung, Security-Maßnahmen, Protokollierung, Entitäten-Map.
Schritt 1: E-Mail-Adresse entgegennehmen (Spec 67%)
1. UI-Lokation
- URL
POST /login- Controller
MagicCodeController::codeAnfordern- Template
src/Auth/Http/Views/magic_code_form.html.php- Zugriff
- oeffentlich
2. Eingabe-Felder (2)
| # | Label | Variable | Typ | Pflicht | Validierung | Max | Ziel |
|---|---|---|---|---|---|---|---|
| 1 | E-Mail-Adresse | email | ja | filter_var FILTER_VALIDATE_EMAIL | 200 | magic_code_login.email | |
| 2 | CSRF-Token | csrf_token | token | ja | hash_equals gegen Session-Token | 64 | —.— |
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (2)
- [application]
src/Auth/Application/MagicCodeAnforderungPort.php→MagicCodeAnforderungPort::anfordern - [http]
src/Auth/Http/MagicCodeController.php→MagicCodeController::codeAnfordern
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (4)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
sql_injection | request.email | prepared_statement | src/Auth/Infrastructure/PdoMensch.php | log_and_block |
| PDO Prepared Statement bei SELECT mensch WHERE email | ||||
header_injection | request.email | strip_crlf | src/Auth/Domain/EmailValidator.php | 400_bad_request |
| Strip \r \n, dann filter_var FILTER_VALIDATE_EMAIL | ||||
xss | request.email | htmlspecialchars | src/Auth/Http/Views/login_form.html.php | log_only |
| Rueckanzeige der Email nur ueber htmlspecialchars mit ENT_QUOTES | ||||
csrf | POST /login | csrf_token | src/Auth/Http/MagicCodeController.php | 403_forbidden |
| Session-gebundener CSRF-Token in Form | ||||
8. Protokollierung (2)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| app_log | info | bei_start | ["email_hash","ip","user_agent","ts"] | {"email":"hmac_sha256_secret_pepper","ip":"ipv4_last_octet_or_ipv6_last_80bit"} | 30 Tage |
| Email-Eingabe Login-Start | |||||
| security_log | warn | bei_fehler | ["email_hash","ip","fehler_code","angriffs_typ"] | {"email":"hmac_sha256_secret_pepper","ip":"ipv4_last_octet_or_ipv6_last_80bit"} | 365 Tage |
| Validierungsfehler Email | |||||
9. Entitaeten-Map (2)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| datei | /var/www/campus.karlkratz.com/src/Auth/Http/Views/magic_code_form.html.php | rendert | /var/www/campus.karlkratz.com/src/Auth/Http/Views/magic_code_form.html.php |
| klasse | MagicCodeController | implementiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | request.email != null AND request.email matches RFC5322 | Email muss vorhanden und syntaktisch valide |
| invariant | keine Persistenz vor Validierung | Email wird erst nach Validierung gehasht |
| post | rate_limit_check.passed = true | Rate-Limit nicht ueberschritten |
11. Persistenz-Schema und Statemachine (0 Mutationen, 0 Zustandsuebergaenge)
Keine definiert.
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 1: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 1: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 1: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 1: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 1: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 2: Rate-Limit pro E-Mail prüfen (Spec 58%)
1. UI-Lokation
Keine UI (System-intern).
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (2)
- gueltig →
weiter: Unter Rate-Limit (max 5 pro Stunde): weiter zu Schritt 3. - ungueltig →
fehler_meldung: Rate-Limit erreicht: HTTP 429, keine weitere Aktion, generische Meldung.
5. Code-Verknuepfung (2)
- [application]
src/Auth/Application/RateLimitPort.php→RateLimitPort::checkEmail - [infrastructure]
src/Auth/Infrastructure/PdoRateLimit.php→PdoRateLimit::checkEmail
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (1)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
rate_limit_bypass | request.ip+email | rate_limit | src/Auth/Application/RateLimiter.php | 429_too_many |
| 5 Versuche pro 15 Minuten pro IP UND Email-HMAC einheitlich, Backoff bei Ueberschreitung | ||||
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| security_log | warn | bei_fehler | ["email_hash","ip","versuche_count"] | {"email":"hmac_sha256_secret_pepper","ip":"ipv4_last_octet_or_ipv6_last_80bit"} | 365 Tage |
| Rate-Limit erreicht | |||||
9. Entitaeten-Map (2)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | RateLimiter | schuetzt | — |
| klasse | EmailValidator | validiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | request.ip != null | IP-Adresse muss bekannt sein |
| invariant | rate_limit_window = 15min konstant | Window-Groesse darf nicht variieren |
| post | attempts_count <= 5 | Versuche im Limit |
11. Persistenz-Schema und Statemachine (0 Mutationen, 1 Zustandsuebergaenge)
| Entitaet | Von | Nach | Ausloeser | Bedingung |
|---|---|---|---|---|
magic_code | sent | blocked | rate_limit_ueberschritten | attempts_count > 5 |
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 2: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 2: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 2: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 2: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 2: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 3: Gate: E-Mail existiert in Mensch? (Spec 75%)
1. UI-Lokation
Keine UI (System-intern).
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (1)
mensch.email(exakt): Case-insensitive Match auf Mensch.email.
4. Gate-Aktionen (2)
- null_treffer →
weiter: Kein Mensch: Sprung zu Schritt 7 (generische Erfolgsmeldung, keine Enumeration). - ein_treffer →
weiter: Mensch gefunden: weiter zu Magic-Code erzeugen (Schritt 4).
5. Code-Verknuepfung (2)
- [application]
src/Auth/Application/MenschPort.php→MenschPort::findeNachEmail - [infrastructure]
src/Auth/Infrastructure/PdoMensch.php→PdoMensch::findeNachEmail
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (2)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
timing_attack | response.email_exists | rate_limit | src/Auth/Application/MagicCodeService.php | log_only |
| Konstante Antwortzeit unabhaengig ob Email existiert, verhindert User-Enumeration | ||||
sql_injection | db.mensch.email_hash | prepared_statement | src/Auth/Infrastructure/PdoMensch.php | log_and_block |
| SELECT mensch WHERE email_hash gebunden | ||||
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| audit_log | info | bei_entscheidung | ["email_hash","mensch_gefunden","ts"] | {"email":"hmac_sha256_secret_pepper","ip":"ipv4_last_octet_or_ipv6_last_80bit"} | 90 Tage |
| Gate Email-Existenz | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | PdoMensch | persistiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | email_hmac != null | HMAC der Email vorhanden |
| invariant | konstante Antwortzeit unabhaengig vom Treffer | Verhindert User-Enumeration |
| post | mensch.id ist null oder int | Eindeutiger Treffer oder kein Treffer |
11. Persistenz-Schema und Statemachine (1 Mutationen, 0 Zustandsuebergaenge)
| Tabelle | Mutation | Felder | Constraints | Atomar mit |
|---|---|---|---|---|
mensch | SELECT | ["id","email_hmac"] | WHERE email_hmac=? LIMIT 1 | — |
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 3: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 3: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 3: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 3: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 3: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 4: Magic-Code erzeugen (Spec 50%)
1. UI-Lokation
Keine UI (System-intern).
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (1)
- [domain]
src/Auth/Domain/MagicCode.php→MagicCode::erzeuge
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (1)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
timing_attack | internal.randomness | random_bytes | src/Auth/Domain/MagicCode.php | 500_internal |
| random_bytes(16) + base32-Encoding für 128-bit Entropie | ||||
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| audit_log | info | bei_erfolg | ["mensch_id","code_hash","expires_at"] | {"email":"hmac_sha256_secret_pepper","ip":"ipv4_last_octet_or_ipv6_last_80bit"} | 90 Tage |
| Magic-Code generiert | |||||
9. Entitaeten-Map (2)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | MagicCode | implementiert | — |
| klasse | MagicCodeService | implementiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | mensch.id != null | Mensch muss existieren (sonst Skip zu Schritt 7) |
| invariant | random_bytes() niemals deterministisch | Crypto-RNG Pflicht |
| post | magic_code.entropy >= 128 ODER magic_code.length 6-8 mit rate_limit | Token oder Code |
11. Persistenz-Schema und Statemachine (0 Mutationen, 0 Zustandsuebergaenge)
Keine definiert.
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 4: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 4: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 4: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 4: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 4: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 5: Magic-Code im Datensatz speichern (Spec 58%)
1. UI-Lokation
Keine UI (System-intern).
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (1)
- [infrastructure]
src/Auth/Infrastructure/PdoMagicCode.php→PdoMagicCode::speichern
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (1)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
sql_injection | db.magic_code | prepared_statement | src/Auth/Infrastructure/PdoMagicCode.php | log_and_block |
| INSERT mit gebundenen Parametern | ||||
8. Protokollierung (2)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| audit_log | info | bei_erfolg | ["mensch_id","code_id","created_at"] | {} | 90 Tage |
| Code persistiert | |||||
| security_log | error | bei_fehler | ["mensch_id","db_fehler"] | {} | 365 Tage |
| DB-Schreibfehler | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | PdoMagicCode | persistiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | magic_code.code_hash != null | Hash bereits berechnet |
| invariant | niemals Klartext-Code in DB | Nur Hash oder HMAC |
| post | magic_code.id != null AND used_at IS NULL | Persistiert, noch nicht eingeloest |
11. Persistenz-Schema und Statemachine (1 Mutationen, 1 Zustandsuebergaenge)
| Tabelle | Mutation | Felder | Constraints | Atomar mit |
|---|---|---|---|---|
magic_code | INSERT | ["mensch_id","code_hash","expires_at","created_ip_hash","attempt_count"] | expires_at=NOW+15min, attempt_count=0 | — |
| Entitaet | Von | Nach | Ausloeser | Bedingung |
|---|---|---|---|---|
magic_code | requested | sent | code_persistiert |
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 5: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 5: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 5: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 5: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 5: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 6: Magic-Code per E-Mail senden (Spec 50%)
1. UI-Lokation
Keine UI (System-intern).
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (2)
- [application]
src/Auth/Application/MailVersandPort.php→MailVersandPort::magicCodeMail - [infrastructure]
src/Auth/Infrastructure/SmtpMailVersand.php→SmtpMailVersand::magicCodeMail
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (2)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
header_injection | mail.to | strip_crlf | src/Auth/Infrastructure/MailSender.php | 400_bad_request |
| Opaker Token mit 128 Bit (random_bytes(16) base32), kein Email in URL, Header Cache-Control no-store | ||||
xss | mail.body | htmlspecialchars | src/Auth/Infrastructure/MailTemplate.php | log_only |
| Escape aller Variablen in Mail-Template | ||||
8. Protokollierung (2)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| app_log | info | bei_erfolg | ["mensch_id","ts","mta_msgid"] | {} | 30 Tage |
| Mail versandt | |||||
| security_log | error | bei_fehler | ["mensch_id","mta_fehler","smtp_code"] | {} | 365 Tage |
| MTA-Fehler | |||||
9. Entitaeten-Map (2)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | MailSender | benachrichtigt | — |
| datei | /var/www/campus.karlkratz.com/src/Auth/Infrastructure/MailTemplate.php | rendert | /var/www/campus.karlkratz.com/src/Auth/Infrastructure/MailTemplate.php |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | magic_code.persisted = true | Code muss zuerst persistiert sein |
| invariant | keine Email in URL | Magic-Link enthaelt nur opaken Token |
| post | mta.queued = true ODER fehler_code != null | Mail in Queue oder definierter Fehler |
11. Persistenz-Schema und Statemachine (0 Mutationen, 0 Zustandsuebergaenge)
Keine definiert.
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 6: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 6: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 6: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 6: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 6: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 7: Generische Erfolgsmeldung zuruecksenden (Spec 50%)
1. UI-Lokation
- URL
GET /login- Controller
MagicCodeController::erfolgsMeldung- Template
src/Auth/Http/Views/login_sent.html.php- Zugriff
- oeffentlich
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (1)
- [http]
src/Auth/Http/MagicCodeController.php→MagicCodeController::erfolgsMeldung
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (0)
Keine definiert.
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| app_log | info | bei_ende | ["ts","response_code"] | {} | 30 Tage |
| Generische Antwort | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| datei | /var/www/campus.karlkratz.com/src/Auth/Http/Views/login_sent.html.php | rendert | /var/www/campus.karlkratz.com/src/Auth/Http/Views/login_sent.html.php |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | immer | Generische Antwort unabhaengig vom Vorergebnis |
| invariant | identische Response fuer existierende und nicht-existierende Email | Constant-Response |
| post | HTTP 200 mit statischem Body | Keine User-Enumeration |
11. Persistenz-Schema und Statemachine (0 Mutationen, 0 Zustandsuebergaenge)
Keine definiert.
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 7: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 7: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 7: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 7: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 7: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 8: Code-Eingabe oder Link-Klick verarbeiten (Spec 67%)
1. UI-Lokation
- URL
POST /login/verify- Controller
MagicCodeController::codeEinloesen- Template
src/Auth/Http/Views/login_verify.html.php- Zugriff
- oeffentlich
2. Eingabe-Felder (2)
| # | Label | Variable | Typ | Pflicht | Validierung | Max | Ziel |
|---|---|---|---|---|---|---|---|
| 1 | Code aus Link | code | token | ja | Alphanum 6 bis 8 Zeichen Grossbuchstaben | 8 | magic_code_login.code |
| 2 | E-Mail aus Link | email | ja | filter_var FILTER_VALIDATE_EMAIL | 200 | magic_code_login.email |
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (1)
- [http]
src/Auth/Http/MagicCodeController.php→MagicCodeController::codeEinloesen
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (3)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
sql_injection | request.code | prepared_statement | src/Auth/Infrastructure/PdoMagicCode.php | log_and_block |
| SELECT magic_code WHERE code=? AND expires_at>NOW() | ||||
csrf | POST /login/verify | csrf_token | src/Auth/Http/MagicCodeController.php | 403_forbidden |
| Session-CSRF-Token bei Code-Eingabe | ||||
brute_force | request.code | rate_limit | src/Auth/Application/RateLimiter.php | 429_too_many |
| Max 5 Code-Versuche pro Email-HMAC innerhalb 15 Min, einheitlich mit Schritt 2 | ||||
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| audit_log | info | bei_start | ["code_hash","ip","ts"] | {"email":"hmac_sha256_secret_pepper","ip":"ipv4_last_octet_or_ipv6_last_80bit"} | 90 Tage |
| Code-Eingabe gestartet | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | MagicCodeController | implementiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | request.code != null UND CSRF-Token gueltig | POST mit CSRF |
| invariant | POST nur, kein GET | Zustandsaendernd |
| post | code_hash berechnet | Hash fuer atomic Lookup |
11. Persistenz-Schema und Statemachine (0 Mutationen, 0 Zustandsuebergaenge)
Keine definiert.
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 8: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 8: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 8: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 8: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 8: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 9: Atomare Code-Einloesung: Pruefen UND Markieren in einer Transaktion (Spec 75%)
1. UI-Lokation
Keine UI (System-intern).
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (1)
magic_code_login.code + email(exakt): Kombination Code und Mail, gültig_bis groesser NOW, eingelöst gleich false.
4. Gate-Aktionen (2)
- gueltig →
weiter: Code gültig und nicht eingelöst: weiter zu Schritt 10. - ungueltig →
fehler_meldung: Code abgelaufen oder unbekannt oder eingelöst: Sprung zu Schritt 12.
5. Code-Verknuepfung (2)
- [application]
src/Auth/Application/MagicCodeCheckPort.php→MagicCodeCheckPort::validiere - [infrastructure]
src/Auth/Infrastructure/PdoMagicCode.php→PdoMagicCode::findeGültig
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (2)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
timing_attack | compare.code | hash_equals | src/Auth/Application/MagicCodeService.php | 500_internal |
| hash_equals statt == für konstante Vergleichszeit | ||||
replay | db.magic_code.used_at | prepared_statement | src/Auth/Infrastructure/PdoMagicCode.php | log_and_block |
| UPDATE code SET used_at=NOW() WHERE used_at IS NULL (atomic) | ||||
8. Protokollierung (2)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| security_log | warn | bei_fehler | ["code_hash","ip","status","versuche"] | {"email":"hmac_sha256_secret_pepper","ip":"ipv4_last_octet_or_ipv6_last_80bit"} | 365 Tage |
| Code-Verify fehlgeschlagen | |||||
| security_log | critical | bei_ausnahme | ["code_hash","ip","versuche_gesamt"] | {"email":"hmac_sha256_secret_pepper","ip":"ipv4_last_octet_or_ipv6_last_80bit"} | 2555 Tage |
| Brute-Force Alarm >5 Versuche | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | MagicCodeService | validiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | code_hash != null | Hash vorhanden |
| invariant | UPDATE WHERE used_at IS NULL muss in einer Transaktion | Race-Condition-frei |
| post | affected_rows = 1 (eingeloest) ODER 0 (ungueltig) | Atomare Aussage |
11. Persistenz-Schema und Statemachine (1 Mutationen, 4 Zustandsuebergaenge)
| Tabelle | Mutation | Felder | Constraints | Atomar mit |
|---|---|---|---|---|
magic_code | UPDATE | ["used_at"] | SET used_at=NOW() WHERE id=? AND used_at IS NULL AND expires_at>NOW (atomic) | — |
| Entitaet | Von | Nach | Ausloeser | Bedingung |
|---|---|---|---|---|
magic_code | sent | verified | code_match_pre_check | Code stimmt, expires_at>NOW |
magic_code | verified | consumed | atomares_update | affected_rows=1 |
magic_code | sent | expired | expires_at_erreicht | expires_at < NOW |
magic_code | verified | blocked | concurrent_attempt | affected_rows=0 trotz Vorpruefung OK |
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 9: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 9: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 9: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 9: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 9: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 10: (Entfaellt: in Schritt 9 atomar enthalten) (Spec 42%)
1. UI-Lokation
Keine UI (System-intern).
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (1)
- [infrastructure]
src/Auth/Infrastructure/PdoMagicCode.php→PdoMagicCode::markiereEingeloest
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (1)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
sql_injection | db.magic_code.used_at | prepared_statement | src/Auth/Infrastructure/PdoMagicCode.php | log_and_block |
| UPDATE mit WHERE id=? AND used_at IS NULL | ||||
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| audit_log | info | bei_erfolg | ["code_id","mensch_id","used_at"] | {} | 2555 Tage |
| Code eingelöst | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | PdoMagicCode | persistiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (0)
Keine definiert.
11. Persistenz-Schema und Statemachine (0 Mutationen, 0 Zustandsuebergaenge)
Keine definiert.
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 10: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 10: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 10: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 10: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 10: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 11: Neue Session für Profil starten (Spec 67%)
1. UI-Lokation
Keine UI (System-intern).
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (2)
- [application]
src/Auth/Application/SessionPort.php→SessionPort::starte - [infrastructure]
src/Auth/Infrastructure/PhpSession.php→PhpSession::starte
6. CRM-Kategorisierung
- Kategorie
- nutzer_aktiv
- Lifecycle-Stage
- retention
- Tags
magic_code_login,returning_user
7. Security-Massnahmen (2)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
csrf | session.new | session_regenerate | src/Auth/Application/SessionStarter.php | 500_internal |
| session_regenerate_id(true) beim Login-Erfolg | ||||
xss | session.user_data | htmlspecialchars | src/Auth/Http/Views/profile.html.php | log_only |
| Alle User-Felder escaped beim Rendern | ||||
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| audit_log | info | bei_erfolg | ["mensch_id","session_id_hmac","login_typ=magic_code","ts"] | {"session_id":"hmac_sha256_secret_pepper"} | 90 Tage |
| Session-Start. session_id NUR als HMAC mit Server-Secret-Pepper, niemals roh. Retention: 90 Tage statt 7 Jahre (kein DSGVO-Pflicht). | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | SessionStarter | implementiert | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | code.eingeloest = true | Code muss eingeloest sein |
| invariant | session_regenerate_id PFLICHT | Session-Fixation-Schutz |
| post | session.id_hmac != null | Session etabliert mit HMAC-Logging |
11. Persistenz-Schema und Statemachine (2 Mutationen, 0 Zustandsuebergaenge)
| Tabelle | Mutation | Felder | Constraints | Atomar mit |
|---|---|---|---|---|
session | INSERT | ["mensch_id","session_id_hmac","login_typ","created_at"] | session_regenerate_id zuvor | — |
audit_log | INSERT | ["mensch_id","session_id_hmac","event_type","outcome","request_id"] | append-only | Schritt-ID 393 |
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 11: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 11: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 11: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 11: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 11: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 12: Fehlermeldung anzeigen (Spec 58%)
1. UI-Lokation
- URL
GET /login/fehler- Controller
MagicCodeController::fehlerSeite- Template
src/Auth/Http/Views/error.html.php- Zugriff
- oeffentlich
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (1)
- [http]
src/Auth/Http/MagicCodeController.php→MagicCodeController::fehlerSeite
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (1)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
xss | response.error | htmlspecialchars | src/Auth/Http/Views/error.html.php | log_only |
| Generische Fehlermeldung, keine User-Daten im Output | ||||
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| app_log | info | bei_fehler | ["fehler_code","generisch"] | {} | 30 Tage |
| Fehlermeldung angezeigt | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| datei | /var/www/campus.karlkratz.com/src/Auth/Http/Views/error.html.php | rendert | /var/www/campus.karlkratz.com/src/Auth/Http/Views/error.html.php |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | fehler_code != null | Definierter Fehler-Code aus Fehlerkatalog |
| invariant | keine User-Daten im Fehler-Body | Information-Disclosure-Schutz |
| post | HTTP 4xx mit generischer Meldung | Keine internen Details |
11. Persistenz-Schema und Statemachine (0 Mutationen, 0 Zustandsuebergaenge)
Keine definiert.
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 12: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 12: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 12: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 12: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 12: transaktional_klar initial geprueft, nach Befund-Fixes 033 |
Schritt 13: Retry-Option anbieten (Spec 58%)
1. UI-Lokation
- URL
GET /login- Controller
MagicCodeController::retryFormular- Template
src/Auth/Http/Views/login_form.html.php- Zugriff
- oeffentlich
2. Eingabe-Felder (0)
Keine Felder.
3. Duplikat-Pruefung (0)
Keine.
4. Gate-Aktionen (0)
Keine.
5. Code-Verknuepfung (1)
- [http]
src/Auth/Http/MagicCodeController.php→MagicCodeController::retryFormular
6. CRM-Kategorisierung
Keine.
7. Security-Massnahmen (1)
| Angriff | Eintrittspunkt | Schutz | Code-Referenz | Bei Verstoss |
|---|---|---|---|---|
rate_limit_bypass | retry.button | rate_limit | src/Auth/Http/MagicCodeController.php | 429_too_many |
| Retry-Button blockiert bei mehr als 5 Versuchen pro 15 Min, einheitlich mit Schritt 2 und 8 | ||||
8. Protokollierung (1)
| Ziel | Level | Trigger | Felder | PII-Maskierung | Retention |
|---|---|---|---|---|---|
| app_log | info | bei_ende | ["retry_angeboten","ts"] | {} | 30 Tage |
| Retry-Option | |||||
9. Entitaeten-Map (1)
| Typ | Ziel | Rolle | Datei |
|---|---|---|---|
| klasse | MagicCodeController | rueckruft | — |
10. Vorbedingungen / Nachbedingungen / Invarianten (3)
| Typ | Ausdruck | Beschreibung |
|---|---|---|
| pre | rate_limit.remaining > 0 | Retry nur wenn Rate-Limit nicht erschoepft |
| invariant | identisches Rate-Limit wie Schritt 2 | 5/15min/IP+Email-HMAC |
| post | redirect zu /login mit Hinweis | Klare UX |
11. Persistenz-Schema und Statemachine (0 Mutationen, 0 Zustandsuebergaenge)
Keine definiert.
12. Quality-Gates (5) — Qualitaets-Score: 100%
| Gate-Typ | Erfuellt | Befund |
|---|---|---|
| korrekt | JA | Schritt 13: korrekt initial geprueft, nach Befund-Fixes 033 |
| widerspruchsfrei | JA | Schritt 13: widerspruchsfrei initial geprueft, nach Befund-Fixes 033 |
| testbar | JA | Schritt 13: testbar initial geprueft, nach Befund-Fixes 033 |
| sicherheits_vollstaendig | JA | Schritt 13: sicherheits_vollstaendig initial geprueft, nach Befund-Fixes 033 |
| transaktional_klar | JA | Schritt 13: transaktional_klar initial geprueft, nach Befund-Fixes 033 |