feat(backend): Master-Passwort fuer die Dokumentenliste (Schritt 5)
GET /documents listet alle Dokumente und machte damit jede UUID auffindbar — das Zugriffsmodell "unerratbarer Link" waere hinfaellig. Der Endpunkt verlangt jetzt HTTP Basic; alles andere bleibt bewusst offen. Ohne konfigurierten Hash ist die Liste ausdruecklich gesperrt (denyAll), nicht offen: Ein vergessener Umgebungswert gaebe sonst jede UUID preis, und niemandem fiele es auf, weil alles funktioniert. Bewusst als Regel und nicht bloss als zufaelliges Passwort, das niemand kennt — der Unterschied ist pruefbar: Mit dem Zufallspasswort blieb die Gegenprobe stumm, mit denyAll faellt genau die danach benannte Zusicherung. Die Sperre nach Fehlversuchen ist global statt je Adresse: Es gibt genau ein Passwort, und hinter dem Reverse Proxy der Zielumgebung saehe der Server ohnehin fuer alle dieselbe Adresse. Preis benannt (D76-Nachtrag 6). 135 Tests. Gegenproben: Schutz entfernt, Sperre entfernt, Voreinstellung geoeffnet -> es faellt jeweils genau die danach benannte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
741b41ef11
commit
bc8850a518
@@ -6160,3 +6160,39 @@ durch Entfernen der Benachrichtigung, dann fällt genau dieses Szenario.
|
||||
`DELETED`, `RESTORED` und `ROLLED_BACK`. Das Umbenennen bekommt seinen eigenen
|
||||
Weg (`PATCH /title` mit `expectedVersion`) und erst damit den Typ — ihn vorher
|
||||
zu deklarieren wäre eine Zusage ohne Deckung.
|
||||
|
||||
**Nachtrag 6 — das Master-Passwort: gesperrt als Voreinstellung, global
|
||||
gesperrt nach Fehlversuchen (2026-08-26).** Drei Festlegungen beim Bauen von
|
||||
Schritt 5:
|
||||
|
||||
**Ohne konfigurierten Hash ist die Liste versperrt, nicht offen.** Die
|
||||
naheliegende Bequemlichkeit — „solange nichts konfiguriert ist, lassen wir
|
||||
durch" — kehrt die Beweislast um: Ein vergessener Umgebungswert gäbe jede
|
||||
Dokument-UUID preis, und niemandem fiele es auf, weil alles funktioniert.
|
||||
Umgekehrt fällt es sofort auf, und das Log sagt beim Start, was fehlt.
|
||||
Umgesetzt ausdrücklich mit `denyAll`, nicht bloß über ein zufälliges Passwort,
|
||||
das niemand kennt: Was gesperrt sein soll, soll auch gesperrt dastehen —
|
||||
nachlesbar und **prüfbar**. Der Unterschied ist nicht theoretisch: Die erste
|
||||
Fassung setzte nur das Zufallspasswort, und die Gegenprobe (den Schutz
|
||||
mutieren, prüfen ob Tests fallen) blieb stumm. Erst mit `denyAll` fällt genau
|
||||
die danach benannte Zusicherung.
|
||||
|
||||
**Die Sperre nach Fehlversuchen ist global, nicht je Adresse.** Es gibt genau
|
||||
ein Passwort; eine globale Sperre ist damit die passende Aussage und nicht zu
|
||||
umgehen, indem jemand die Adresse wechselt. Sie hängt außerdem nicht an
|
||||
`X-Forwarded-For` — hinter dem Reverse Proxy der Zielumgebung (Nachtrag 1)
|
||||
sähe der Server für alle dieselbe 127.0.0.1, und eine „adressbezogene" Sperre
|
||||
wäre unfreiwillig doch global, nur schlechter begründet. Der Preis ist
|
||||
benannt: Wer falsch rät, sperrt die Liste für alle, 15 Minuten lang. Die Liste
|
||||
ist eine Bequemlichkeit für den Betreiber; die Dokumente selbst bleiben über
|
||||
ihre UUID erreichbar.
|
||||
|
||||
**Der Hash trägt sein Verfahren als Präfix** (`{bcrypt}$2a$…`, Spring
|
||||
Securitys `DelegatingPasswordEncoder`). So steht in der Konfiguration, womit
|
||||
gehasht wurde, ein Wechsel des Verfahrens bricht nichts — und die Tests dürfen
|
||||
`{noop}` benutzen, ohne dass dafür eine zweite Code-Bahn nötig wäre.
|
||||
|
||||
**Die neue Laufzeit-Abhängigkeit** (`spring-boot-starter-security`) ist in D76
|
||||
ausdrücklich vorgesehen („geprüft über Spring Security") und damit von der
|
||||
Rückfragepflicht der Wurzel-CLAUDE.md gedeckt. Sie ist zugleich der Platz für
|
||||
die spätere richtige Authentifizierung.
|
||||
|
||||
Reference in New Issue
Block a user