Security
-
[GHSA-rpg6-935f-pp7v] Remote memory exhaustion via unbounded JSON body on
POST /create/secret(High, CVSS 7.5)
The handler decoded the request body without a size cap, so an unauthenticated client could
force the server to allocate an arbitrarily large payload in memory before the--max-length
check ran. A single large request could OOM-kill the process. The body is now bounded with
http.MaxBytesReaderand oversized requests get a 413 without being buffered. (#3772)
Reported by @EQSTLab. -
Session cookies stayed valid after logout, and for far longer than intended. (#3775)
securecookie.Newdefaults to a 30-day maxAge, so the 24hMaxAgeon the cookie was a
client-side hint only — a captured cookie authenticated for 30 days. The codec maxAge now
matches. Sessions also carry a random ID; logout records it as revoked with a 24h TTL and
getSessionrejects any cookie whose ID is listed, so a replayed cookie no longer
authenticates. Revocation lookups fail open, so a storage outage does not sign everyone out.
Affects deployments running OIDC with--require-auth. -
Unbounded Prometheus label cardinality from client-supplied HTTP methods. (#3773)
Arbitrary methods reaching the file-server catch-all route each created a new metric label,
letting an unauthenticated client grow server memory indefinitely. Unknown methods now collapse
to the existing<other>sentinel. -
HTTP servers had no connection timeouts. (#3776)
A client could open a connection and hold it open indefinitely without completing a request.
ReadHeaderTimeoutandIdleTimeoutare now set on the yopass server, and the full set
includingReadTimeout/WriteTimeouton the metrics server. Whole-request deadlines are
intentionally left off the yopass server because file upload and download stream bodies of
arbitrary size.
Bug fixes
- A transient storage error no longer destroys a multi-view file secret. (#3774)
FileStore.Loadreturned an undifferentiated error for both "object is gone" and "store is
unreachable", and the download handler deleted the database metadata in either case — so a
single S3 network blip or disk hiccup permanently destroyed the secret.Loadnow wraps the
ErrKeyNotFoundsentinel on definite not-found only (os.IsNotExist, S3NoSuchKey/NotFound,
database cache miss). The handler cleans up stale metadata for that case alone; anything else
returns 503 and keeps the record so the secret survives the outage.
Hardening
-
Stricter PGP armor validation on submission. (#3772)
isPGPEncryptedonly parsed the armor
BEGIN line, so any string shaped like an armor header was accepted — wrong block types,
truncated messages, plaintext wrapped in headers. The block type, body, and END marker are now
all verified. Thanks to @xhydraxOffSec for reporting this.This is input hygiene, not a security boundary: a client can always encrypt with a key it
controls, so the server can never prove a payload is encrypted. The value is rejecting
malformed ciphertext before a sender shares a link to a secret nobody can decrypt. SECURITY.md
is reworded accordingly — "No Plain Text Storage" is guaranteed by the client, not enforced by
the server.
Behavior changes
- Malformed or truncated armored payloads that earlier versions accepted are now rejected with
400. Requests exceeding the body cap return 413 instead of 400. Clients producing well-formed
OpenPGP messages are unaffected. - Logging out now invalidates the session server-side. Existing sessions are unaffected by the
upgrade, but cookies issued before it are subject to the corrected 24h codec lifetime.