Harden uploads + production transport (security audit H1–H3, M1)
Harden uploads + production transport (security audit H1–H3, M1)
#50 in Riparion/riparion-cms — merged 2026-06-03
Implements the agreed remediation bundle from the full-app security audit.
Findings fixed
| # | Sev | Fix |
|---|---|---|
| H1 | High | Upload memory-exhaustion DoS — add tower-http RequestBodyLimitLayer (24 MiB) to the router so Dioxus server fns (which read the whole body themselves, bypassing axum's default 2 MiB limit) can't be driven to OOM. Also reject oversized base64 before decoding in upload_media. |
| H2 | High | Decompression-bomb OOM — decode uploads through image::ImageReader with explicit max_image_width/height (10k px/side) instead of unbounded load_from_memory, so a tiny file declaring huge dimensions can't allocate GBs during to_rgba8/resize. |
| H3 | High | Session cookie shipped without Secure/HSTS — enable cookie_secure(true) + HSTS, gated on SITE_URL being https:// (or DX_COOKIE_SECURE=1) so local dx serve over http stays loginnable. HSTS conservative by default (max-age=86400; includeSubDomains, no preload); ramp via DX_HSTS. |
| M1 | Med | Content smuggling — validate uploaded bytes are an allowed raster image via magic-byte sniff (image::guess_format); the extension alone is client-controlled. |
Dropped
- L3 (ServeDir symlink hardening):
tower-http0.6 exposes nofollow_symlinkstoggle, and the upload path can't create symlinks (filename sanitizer +{id}_prefix), so there's no app-reachable exploit. Noted rather than hacked around.
Not included (need a product decision)
- M2 (pages have no per-object authz): fine if
PAGES_WRITEstays admin-only; needs acan_edit_pagecheck only if page authoring is delegated.
Notes
Cargo.lockalso carries a pre-existing arium pin bump (already running in prod) — it provides thecookie_secure/hstsbuilder methods H3 relies on.- L6 (default
postgres:postgresDB password) was already fixed out-of-band during the prod migration.
Verification
cargo fmt --all -- --check✅cargo clippyonserver,sqlite/server,postgres/web(wasm32) — all-D warningsclean ✅cargo test --features server,sqlite— 42 passed ✅ (incl. the rendition tests exercising the rewritten decode path)
🤖 Generated with Claude Code
Last updated 2026-06-03
Links to this note
Credits
Merged pull requests, newest first.