Gefunden beim Nachsehen im Protokoll nach der ersten echten Anmeldung:
Jeder Eintrag trug dieselbe IP 172.69.220.140 -- eine Cloudflare-Adresse.
Ursache: Die Kette ist Besucher -> Cloudflare -> Caddy -> Express, aber
`trust proxy` steht auf 1. Express nimmt daher den letzten Eintrag aus
X-Forwarded-For, und das ist Cloudflare.
Auswirkung war nicht nur ein unbrauchbares Protokoll, sondern vor allem:
ALLE Nutzer teilten sich einen einzigen Sperr-Zaehler. Beim ersten
Anmelden waren nach zwei Tippfehlern plus drei Testversuchen bereits
5 von 8 verbraucht -- drei weitere und der Zugang waere fuer alle
gesperrt gewesen.
Jetzt wird CF-Connecting-IP ausgewertet (setzt Cloudflare bei jeder
Anfrage selbst, vom Besucher nicht faelschbar), mit Rueckfall auf die
Peer-Adresse. `trust proxy` bleibt bewusst unangetastet, damit die
Aenderung nur /workspace betrifft und nicht die ganze Website.
Geprueft: 8 Fehlversuche von IP A sperren IP A (429), IP B bekommt
weiterhin 401 und kann sich normal anmelden (200).
Ausserdem: Spaltenbreite im Protokoll-Ausdruck korrigiert -- bei
`anmeldung_fehlgeschlagen` (genau 24 Zeichen) klebte die Rolle am Namen.
Erster funktionierender Login fuer /workspace. Bewusst OHNE neue
Abhaengigkeiten: Node 24 bringt node:sqlite mit, Hashing und Zufall
kommen aus node:crypto. Nichts zu kompilieren, keine fremde Lieferkette
an einer Stelle, an der es um Zugangsdaten geht.
Sicherheit:
- Codes liegen nur als scrypt-Hash (N=32768) mit eigenem Salt in der DB
- Vergleich in konstanter Zeit (timingSafeEqual)
- Sitzungstoken 32 Byte Zufall, in der DB nur als SHA-256
- Cookie httpOnly, SameSite=lax, Path=/workspace, secure abhaengig von
req.secure (live immer an, nur der lokale http-Test kommt ohne aus)
- Sperre nach 8 Fehlversuchen je IP fuer 10 Minuten -- danach ist auch
der richtige Code blockiert (geprueft)
- gleiche Fehlermeldung bei falscher Rolle und falschem Code
- Audit-Log fuer Anmeldung, Fehlversuch, Sperre, Abmeldung, Codewechsel
Die Datenbank liegt AUSSERHALB des Repos (../workspace-daten/): sonst
waere sie ueber express.static aus dem Netz erreichbar, und ein git pull
wuerde Nutzdaten anfassen.
workspace.js kann die Website nicht mitreissen: node:sqlite wird erst bei
Bedarf per createRequire geladen, jede Route faengt ihre Fehler selbst ab.
Faellt die DB aus, antwortet nur /workspace/api/* mit 503.
Codes werden ausschliesslich auf der Kommandozeile erzeugt
(server/workspace-code.js) und dort einmal angezeigt -- nie in Git.
Ausserdem: start.html als geschuetzte Seite nach dem Anmelden. Der Schutz
sitzt serverseitig vor express.static, nicht nur im Browser.