Unauthenticated login/signup decode an unbounded, un-timed request body before any credential check
medium Possibly Valid medium confidence
Status
Possibly Valid
The unauthenticated unbounded/un-timed body decode is fully code-proven (public endpoints, no MaxBytesReader, no ReadTimeout). The availability impact is gated on host limits and fronting-proxy body timeouts, so possibly_valid — the strongest DoS finding here because it is pre-auth. Canonical of the pair (100054).
Repository / Component
Plain-English Description
Anyone on the internet can hit the login and signup pages without an account, and the server reads their entire request into memory before checking any password. There is no limit on how big or how slow that request can be, so an unauthenticated attacker can push the server to consume excessive memory or hold…
Description of the Underlying Issue
The /api/login and /api/signup routes are registered with handle()/monkey() and no withUser middleware (http/http.go:49-50), so they are reachable without authentication. On these paths the request body is JSON-decoded before any credential is checked: auth/json.go:40 (json.NewDecoder(r.Body).Decode(&cred)), auth/hook.go:46 (identical decode in hook auth mode), and http/auth.go:155 (signup decode). The handle() wrapper passes r.Body through without http.MaxBytesReader (http/data.go:50-85), and repo-wide there is no MaxBytesReader/io.LimitReader on the HTTP path. The server sets only ReadHeaderTimeout (cmd/root.go:246-249) with no ReadTimeout/IdleTimeout/WriteTimeout, so a slow body sent after the headers is never reaped. The result is an unauthenticated, unbounded, un-timed pre-auth body read.
Potential Attack
An unauthenticated attacker POSTs to /api/login (or /api/signup) either a very large JSON body to force large in-memory allocation during decode, or a slowloris-style body dribbled a few bytes at a time to hold the connection and its goroutine open indefinitely. No credentials are required and no size or time limit intervenes before the body is consumed.
Outcomes of Potential Attack
Denial of service via memory pressure (large-body decode) and/or connection and goroutine accumulation (slow bodies), reachable pre-authentication. Because the endpoint is always public and unauthenticated, this is the lowest-barrier availability attack in the batch — no account or prior access is needed.
Affected Scope
Unauthenticated POST /api/login and /api/signup (and hook auth): the request body is JSON-decoded with no size limit before any credential check, and there is no read timeout.
Suggested Fix (plain english)
Cap the size of the login and signup request bodies and add a read timeout, so an unauthenticated request cannot be arbitrarily large or arbitrarily slow.
Suggested Fix (detailed)
Wrap the request body on the public login/signup paths with http.MaxBytesReader using a small fixed limit (credentials JSON is tiny) before decoding, at auth/json.go:40, auth/hook.go:46, and http/auth.go:155 — or centrally in the handle() wrapper (http/data.go). Set http.Server ReadTimeout and IdleTimeout in cmd/root.go so slow bodies are reaped. Add rate limiting (per-IP) to /api/login and /api/signup. Verify by posting an over-limit body (expect 413) and a stalled slow body (expect the connection to be closed after the timeout). This finding is the canonical record for the pair; DOK-100054 is a re-verified duplicate merged here. Wrap pre-auth bodies with http.MaxBytesReader, set server ReadTimeout/IdleTimeout, and consider rate limiting the login/signup endpoints.
Validation
The unauthenticated unbounded/un-timed body decode is fully code-proven (public endpoints, no MaxBytesReader, no ReadTimeout). The availability impact is gated on host limits and fronting-proxy body timeouts, so possibly_valid — the strongest DoS finding here because it is pre-auth. Canonical of the pair (100054).
Full Evidence
http/http.go:49-50 /api/login and /api/signup registered via handle() only (no withUser) → pre-auth reachable
auth/json.go:40 json.NewDecoder(r.Body).Decode(&cred) before any credential check
auth/hook.go:46 identical unbounded pre-auth decode in hook mode
http/auth.go:155 signup json.NewDecoder(r.Body).Decode(info)
http/data.go:50-85 handle() passes r.Body through with no MaxBytesReader
cmd/root.go:246-249 only ReadHeaderTimeout; no ReadTimeout/WriteTimeout/IdleTimeout
repo-wide grep: no MaxBytesReader/io.LimitReader; LimitListener/rate.Limiter absent on the HTTP path
Proven fact: Public pre-auth endpoints decode an unbounded request body before authentication, and the server sets no body-read/idle timeout — complete in source and reachable unauthenticated
Unvalidated fact: Whether a large body actually exhausts memory, and whether slow bodies exhaust connections/goroutines, depends on host memory/limits and whether a fronting reverse proxy enforces body-read timeouts — runtime facts
http/http.go:49-50 — /api/login and /api/signup registered via handle()/monkey() with no withUser, so pre-auth reachable
auth/json.go:40 — json.NewDecoder(r.Body).Decode(&cred) before any credential check
auth/hook.go:46 — identical unbounded pre-auth decode in hook auth mode
http/auth.go:155 — signup json.NewDecoder(r.Body).Decode(info)
http/data.go:50-85 — handle() passes r.Body through with no MaxBytesReader
cmd/root.go:246-249 — only ReadHeaderTimeout; no ReadTimeout/WriteTimeout/IdleTimeout
information-results DOK-100049 (satisfied): MaxBytesReader/MaxBytes appears nowhere in the Go codebase; no LimitListener/rate limiter on the HTTP path; Dockerfile execs filebrowser directly with no bundled reverse proxy and no docs mandating one
Proven fact: /api/login and /api/signup are always public (no auth middleware) — verified in http/http.go
Proven fact: The request body is JSON-decoded before any credential check on these public paths (auth/json.go:40, auth/hook.go:46, http/auth.go:155)
Proven fact: No body-size cap (MaxBytesReader/LimitReader) exists anywhere in the Go codebase
Proven fact: The server sets no ReadTimeout/IdleTimeout, so post-header slow bodies are un-timed (cmd/root.go:246-249)
Proven fact: The shipped container provides no default fronting reverse proxy
Unvalidated fact: Whether a large body actually exhausts host memory, and whether slow bodies actually exhaust connections/goroutines, depends on host memory/limits and whether an operator-added fronting reverse proxy enforces body-size/read-time limits — runtime/deployment facts
http/http.go:49-50 — /api/login and /api/signup are registered with handle() only (no withUser) => attacker-reachable pre-auth
auth/json.go:36-43 — json.NewDecoder(r.Body).Decode(&cred) executes before any credential check
auth/hook.go:42-49 — identical unbounded pre-auth decode in hook mode
http/auth.go:150-158 — signup json.NewDecoder(r.Body).Decode(info)
http/data.go:50-85 — handle() never bounds r.Body
cmd/root.go:246-249 — http.Server{ReadHeaderTimeout:60s} only; no ReadTimeout/WriteTimeout/IdleTimeoutrepo-wide grep: MaxBytesReader|io.LimitReader => No matches
grep: LimitListener|rate.Limiter => only img/service.go semaphore (not on the HTTP path)