v1.0.11 Security Fix -- no more zip bombz
[1.0.11] — 2026-03-06
Security — Critical
-
CRIT-1: Security headers — added
Content-Security-Policy,Strict-Transport-Security,
andPermissions-Policyresponse headers tobuild_router(). CSP restricts scripts, styles,
and media to'self', preventing external payload loading or data exfiltration via XSS.
HSTS enforces HTTPS for one year including subdomains. Permissions-Policy disables
camera, microphone, and geolocation APIs. -
CRIT-2: Proxy-aware IP extraction in post handlers — all post-creation handlers
(create_thread,post_reply) now use the proxy-awareextract_ip()/ClientIpextractor
instead of the raw socket address. Bans and rate limits are now effective when the server
runs behind nginx or any other reverse proxy. -
CRIT-3: Rate limiting on GET endpoints — the catalog, search, thread-update, and JSON
API endpoints were previously completely unrate-limited, allowing trivial DoS via unbounded
LIKE scans or 200-thread catalog loads. A separate GET rate limiter (60 req/min per IP)
has been applied to all read-heavy routes. -
CRIT-4: Zip-bomb protection on restore handlers — all four backup restore handlers
previously usedstd::io::copywith no size or entry limits. Each entry is now capped at
1 GiB via.take()and extraction aborts if more than 50 000 entries are encountered,
preventing a 1 KB zip from exhausting disk space. -
CRIT-5: IP address hashing — raw IP addresses are no longer stored in the
ACTIVE_IPS
DashMapor printed to stdout/logs. All IP tracking now uses the same HMAC-keyed hash
(hash_ip) used elsewhere, preventing IP exposure in coredumps or log aggregators. -
CRIT-6: Admin login brute-force lockout — the admin login endpoint previously had no
per-IP failure tracking beyond the global rate limit (~600 attempts/hour). Failed login
attempts are now counted per IP and the account is locked for a progressive delay after
5 consecutive failures. -
CRIT-7: Constant-time CSRF token comparison — CSRF token validation was using
standard==string comparison, leaking prefix-matching information via timing side
channel. Comparison now usessubtle::ct_eqfor constant-time equality. -
CRIT-8: Poll input length and count caps — poll questions and options had no
server-side limits, allowing megabytes of text or thousands of options per submission.
Poll options are now capped at 10, each option at 128 characters, and the question
at 256 characters.
Security — High
-
HIGH-1: Admin session cookie
Max-Age— the session cookie previously had no
Max-AgeorExpiresattribute, causing browsers to persist it indefinitely after the
tab was closed. The cookie now carries aMax-Agematching the server-side
session_durationconfig value. -
HIGH-2: Database connection pool timeout — the r2d2 connection pool had no
acquisition timeout, allowingspawn_blockingthreads to block forever under load and
exhaust the Tokio thread pool. A 5-secondconnection_timeouthas been added to the
pool builder. -
HIGH-3: Per-route body limits on small-payload endpoints — the global 50 MiB
DefaultBodyLimitwas applied to every route including login, vote, report, and appeal,
causing the server to buffer 50 MiB before returning 400 on oversized requests. These
four endpoints now carry an explicit 64 KiB per-route limit. -
HIGH-4: Open redirect hardening on
return_to— the logoutreturn_toparameter
check only blocked//and.., allowing backslash (\) and URL-encoded variants
(%5C) to redirect to external hosts on some browsers. The filter now also rejects any
value containing a literal backslash or its percent-encoded form. -
HIGH-5: Proxy-aware IP in
file_reportandsubmit_appeal— these two handlers
were using the raw socket IP for per-IP rate limiting, making the limit ineffective
behind a reverse proxy. Both now use theClientIpextractor, consistent with post
handlers (same root cause as CRIT-2). -
HIGH-6: Exponential backoff with jitter in worker error recovery — all four
background workers recovered from DB errors with a flat 2-second sleep, meaning all
workers could retry simultaneously and storm the database. Error recovery now uses
exponential backoff (500 ms base, doubling per failure, capped at 60 s) with 0–500 ms
random jitter to spread retries across workers. -
HIGH-7: TOCTOU race in file deduplication — concurrent identical uploads could both
pass the hash check before either had written tofile_hashes, causing the second
record_file_hashcall to return a 500. The insert now usesINSERT OR IGNOREso the
second concurrent insert is silently a no-op instead of an error.