Password-equivalent share token carried in URL query string with no Referrer-Policy (CWE-598)
low Possibly Valid medium confidence
Status
Possibly Valid
Code facts are proven: a password-equivalent token is accepted from and echoed into the URL query with no Referrer-Policy (CWE-598). Held at possibly_valid/low because realized compromise requires an external leak channel that source reading cannot confirm; the token mechanism itself is partly by design, so the finding is the query-string exposure surface.
Repository / Component
Plain-English Description
A password-protected share can be opened with a secret token instead of the password, and that token travels in the web address. Web addresses commonly end up in server logs, browser history, and forwarded links, so anyone who sees the address can reopen the share without knowing the password.
Description of the Underlying Issue
Presenting ?token=<link.Token> to the public share endpoint fully bypasses the share password (public.go:130-136): the handler accepts access when r.URL.Query().Get("token") == l.Token, with no password check. The token is a password-equivalent credential, yet it is transported in the URL query string and is also emitted into the public FileInfo JSON (public.go:50/76, file.go:51 `Token string json:"token,omitempty"`), so it appears in request URLs by design. No Referrer-Policy header is set anywhere — globalHeaders (headers.go) defines only Cache-Control — so the token can leak via the Referer header and other URL-recording channels. This is a credential-in-URL exposure surface (CWE-598), not a token-forgery or brute-force weakness.
Potential Attack
A user opens a password-protected share via .../api/public/dl/{hash}?token=<token>. That full URL is recorded by an intermediary (reverse/forward-proxy or CDN access log that logs query strings), saved in browser history, or forwarded by the recipient. A party who later reads the URL replays ?token=<token> and is granted access at public.go:134 without ever knowing the share password.
Outcomes of Potential Attack
An unauthorized party gains full access to the password-protected share's contents, defeating the password gate, without cracking or guessing anything — they simply reuse a leaked token.
Affected Scope
A single password-protected share whose token-bearing URL (?token=<link.Token>) is exposed through an external leak channel.
Suggested Fix (plain english)
Send the share token in a request header or POST body instead of the URL, and set a restrictive Referrer-Policy on share responses.
Suggested Fix (detailed)
Transport the token via a request header (or POST body) rather than the query string so it is not captured in URL-recording channels; set Referrer-Policy: no-referrer (or same-origin) on share responses; and document the URL-leak risk for password-protected shares. Verify that no share access path accepts the token from r.URL.Query() and that share responses carry the Referrer-Policy header. Transport the token via a request header or POST body instead of the query string; set a restrictive Referrer-Policy (e.g. no-referrer) on share responses; document the URL-leak risk for password-protected shares.
Validation
Code facts are proven: a password-equivalent token is accepted from and echoed into the URL query with no Referrer-Policy (CWE-598). Held at possibly_valid/low because realized compromise requires an external leak channel that source reading cannot confirm; the token mechanism itself is partly by design, so the finding is the query-string exposure surface.
Full Evidence
http/public.go:134 r.URL.Query().Get("token") == l.Token grants access with no passwordhttp/public.go:50,76 files.NewFileInfo Token: link.Token
files/file.go:51 Token string `json:"token,omitempty"` -> token returned in the public JSON
http/headers.go globalHeaders defines only Cache-Control (no Referrer-Policy)
http/http.go:32 global CSP default-src 'self'
Proven fact: The token is a password-equivalent credential: presenting ?token=<link.Token> fully bypasses the share password (public.go:130-136).
Proven fact: The token is transported in the URL query string and emitted in the public FileInfo JSON (public.go:50/76, file.go:51), so it appears in request URLs by design.
Proven fact: No Referrer-Policy header is set anywhere; globalHeaders (headers.go) defines only Cache-Control: no-cache,no-store,must-revalidate.
Proven fact: The token is high-entropy (96 random bytes, share.go:149-153), so the risk is exposure/leakage, not brute force.
Unvalidated fact: Whether a leak channel exists and is attacker-accessible in a given deployment: reverse/forward-proxy or CDN access logs that record query strings, shared browser history, or recipient link forwarding.
Unvalidated fact: The precise extent to which CSP default-src 'self' suppresses the automatic cross-origin Referer vector (manual leak vectors are unaffected regardless).
http/public.go:134 — r.URL.Query().Get("token") == l.Token grants access with no passwordhttp/public.go:50,76 — files.NewFileInfo populated with Token: link.Token
files/file.go:51 — Token string `json:"token,omitempty"` returns the token in public JSON
http/headers.go — globalHeaders defines only Cache-Control (no Referrer-Policy)
http/http.go:32 — global CSP default-src 'self'
http/share.go:149-153 — token is 96 random bytes (high entropy)
Proven fact: Presenting ?token=<link.Token> fully bypasses the share password (public.go:130-136); the token is a password-equivalent credential.
Proven fact: The token is transported in the URL query string and emitted in the public FileInfo JSON, so it appears in request URLs by design.
Proven fact: No Referrer-Policy header is set anywhere; globalHeaders defines only Cache-Control: no-cache,no-store,must-revalidate.
Proven fact: The token is high-entropy (96 random bytes), so the risk is exposure/leakage, not brute force.
Unvalidated fact: Whether a leak channel exists and is attacker-accessible in a given deployment: proxy/CDN access logs that record query strings, shared browser history, or recipient link forwarding.
Unvalidated fact: The precise extent to which CSP default-src 'self' suppresses the automatic cross-origin Referer vector; manual leak vectors (logs, history, forwarding) are unaffected regardless.
http/public.go:50 and public.go:76 NewFileInfo Token: link.Token
files/file.go:51 Token string `json:"token,omitempty"` -> token returned in public JSON
http/headers.go globalHeaders = only Cache-Control; no Referrer-Policy
http/http.go global CSP default-src 'self' blocks cross-origin Referer but not logs/history/forwarding