security(sharing): Selbsttest fuer Freigabelinks, drei Befunde behoben
Freigabelinks sind der einzige Teil von Aether, den man ohne Anmeldung erreicht -- und hatten als einziger Bereich keinen einzigen Test. Der neue scripts/sharing-selbsttest.py geht sie mit 24 Pruefungen ab, mit einem eigens angelegten Zweitnutzer, ohne den sich "fremde Dateien bleiben fremd" gar nicht pruefen laesst. Beim ersten Lauf sofort drei Befunde: 1. Der Download-Zaehler blieb auf 0. Die Zaehlung lief in einer Goroutine mit dem Kontext der Anfrage -- der wird abgebrochen, sobald die Antwort draussen ist, und die Zaehlung verlor das Rennen fast immer. Jetzt context.WithoutCancel mit eigener Frist. 2. owner_id, file_id und die Link-Kennung standen in der oeffentlichen Antwort. Das ganze Datenbankobjekt durchzureichen war bequem, gab aber die Kennung des Besitzers an jeden heraus, der den Link hat: Zwei Links desselben Besitzers liessen sich einander zuordnen, ohne dass irgendwer angemeldet ist. Jetzt nur noch, was die oeffentliche Seite braucht. 3. Passwoerter liessen sich unbegrenzt durchprobieren. Der oeffentliche Freigabepfad lag ausserhalb jeder Ratenbegrenzung, und weil jeder Versuch Argon2 rechnet, war das zugleich ein billiger Weg, den Server zu beschaeftigen. Jetzt dieselbe Fehlversuchssperre wie bei /dav und /caldav: zehn Fehlschlaege in 15 Minuten, dann 15 Minuten Pause. Die Sperre hat eine Folge fuers Testen selbst: Nach einem Lauf ist die eigene IP 15 Minuten fuer passwortgeschuetzte Links gesperrt. Deshalb steht die Probe am Ende, und der Test erkennt zu Beginn, ob eine Sperre aus einem frueheren Lauf noch steht, statt reihenweise Fehlschlaege zu melden, die keine sind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1e1f35955d
commit
ec8f162fd5
5 files changed
+333
-11
No files matched your search
+40
@@ -95,6 +95,46 @@ Dazu eine Ungenauigkeit ohne Folgen: Ein PUT, das einen vorhandenen Termin
|
||||
überschreibt, antwortet mit `201 Created` statt `204 No Content`. Das kommt
|
||||
aus go-webdav; Clients prüfen auf 2xx.
|
||||
|
||||
## Freigabelinks: Selbsttest per Skript
|
||||
|
||||
Freigabelinks sind der **einzige** Teil von Aether, den man ohne Anmeldung
|
||||
erreicht. Ein Fehler darin ist kein Anzeigefehler, sondern eine offene Tür.
|
||||
|
||||
python3 scripts/sharing-selbsttest.py
|
||||
|
||||
24 Prüfungen: anlegen nur angemeldet und nur für **eigene** Dateien (auch der
|
||||
Admin kommt nicht an fremde), Tokenlänge, öffentlicher Abruf ohne Anmeldung,
|
||||
Herunterladen byte-genau, Passwortschutz (ohne, falsch, richtig),
|
||||
Download-Zähler, Widerrufen (danach ist der Link tot), ein Link auf eine
|
||||
gelöschte Datei liefert nichts, und die Sperre nach wiederholten
|
||||
Fehlversuchen.
|
||||
|
||||
Der Test legt einen Zweitnutzer an – ohne ihn lässt sich „fremde Dateien
|
||||
bleiben fremd" gar nicht prüfen – und räumt ihn samt Dateien wieder weg.
|
||||
|
||||
**Nach einem Lauf ist die eigene IP 15 Minuten gesperrt**, für
|
||||
passwortgeschützte Freigabelinks (nicht fürs Anmelden). Das ist der Preis der
|
||||
letzten Prüfung, die genau diese Sperre nachweist. Ein zweiter Lauf innerhalb
|
||||
der Frist bricht früh ab und sagt es. Beim Entwickeln hilft ein
|
||||
`docker restart aether-app` – die Sperre liegt im Arbeitsspeicher.
|
||||
|
||||
### Was dieser Test beim ersten Lauf gefunden hat
|
||||
|
||||
Drei Dinge, alle behoben – hier notiert, weil sie zeigen, wonach man in
|
||||
diesem Bereich sucht:
|
||||
|
||||
- **Der Download-Zähler blieb auf 0.** Die Zählung lief in einer Goroutine
|
||||
mit dem Kontext der Anfrage – der wird abgebrochen, sobald die Antwort
|
||||
draußen ist, und die Zählung verlor das Rennen fast immer.
|
||||
- **`owner_id` stand in der öffentlichen Antwort.** Das ganze
|
||||
Datenbankobjekt durchzureichen war bequem, gab aber die Kennung des
|
||||
Besitzers an jeden heraus, der den Link hat: Zwei Links desselben
|
||||
Besitzers ließen sich einander zuordnen, ohne dass irgendwer angemeldet
|
||||
ist.
|
||||
- **Passwörter ließen sich unbegrenzt durchprobieren.** Der öffentliche
|
||||
Freigabepfad lag außerhalb jeder Ratenbegrenzung; jeder Versuch rechnet
|
||||
Argon2, war also zugleich ein billiger Weg, den Server zu beschäftigen.
|
||||
|
||||
## Notfall: Migration hängt fest ("Dirty database version")
|
||||
|
||||
Betrifft jede künftige Migration, nicht nur eine bestimmte. Nachgestellt und
|
||||
|
||||
Reference in new issue
Block a user