A file can come back out, and a script cannot come back forever

Two halves of the same evening.

Uploads were one-way. A `type: file` field put an object in the bucket
and wrote its key into the record, and nothing ever read it back - a
desk showed `0aba5734-...-drawing.pdf` as a string with no link, so a
builder could receive drawings and never open one.
`GET /attachment/{bucket}/{record}/{field}` streams it. The key is not
the credential and is not taken from the caller: it is read out of the
record named, so a file is reachable only through a record somebody is
allowed to see, and a key that leaks off a desk is a link to nothing.

And there was no rate limiting anywhere. A burst of 30 per caller IP,
refilled one every two seconds, outside the session layer so anyone
over it is turned away before a handler is entered. Well above a
person filling in a form - a page load fetches several resources at
once - and well below being a nuisance. PORTAL_RATE_BURST and
PORTAL_RATE_SECONDS retune it; either 0 turns it off for an instance
behind something that already does this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-28 16:28:43 +02:00
co-authored by Claude Opus 5
parent 8d6f29011c
commit d5de25e8c6
6 changed files with 415 additions and 3 deletions
+2
View File
@@ -63,6 +63,7 @@ web-sys = { version = "0.3", features = [
"Response",
"Window",
], optional = true }
tower_governor = { version = "0.8.0", optional = true }
[features]
default = []
@@ -81,6 +82,7 @@ ssr = [
"dep:leptos_axum",
"dep:axum",
"dep:aws-sdk-s3",
"dep:tower_governor",
"dep:tokio",
"dep:tower",
"dep:tower-http",