fix(security): env-configurable trustProxy with a safe default (#61)
trustProxy was unconditionally true, so req.ip came from a client-forgeable X-Forwarded-For and the per-IP throttles (share-AI, /mcp brute-force) were spoofable. Make it env-configurable (TRUST_PROXY) with a safe default that trusts XFF only from loopback/private proxies, documented in .env.example. NOTE: this changes the default from trust-all; deployments whose proxy is on a public IP must set TRUST_PROXY (caveat documented). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
25
.env.example
25
.env.example
@@ -3,14 +3,31 @@ APP_URL=http://localhost:3000
|
||||
PORT=3000
|
||||
|
||||
# --- Security / reverse proxy ---
|
||||
# The app runs with Fastify `trustProxy` ENABLED, so it derives the client IP
|
||||
# (req.ip) from the `X-Forwarded-For` header. That header is client-forgeable.
|
||||
# Deploy this app behind a trusted reverse proxy that SETS/OVERWRITES (not
|
||||
# appends) `X-Forwarded-For` with the real client IP. Without such a proxy, any
|
||||
# The app derives the client IP (req.ip) from the `X-Forwarded-For` header via
|
||||
# Fastify `trustProxy`. That header is client-forgeable, so XFF is trusted only
|
||||
# from proxies on the configured trusted networks. Deploy this app behind a
|
||||
# trusted reverse proxy that SETS/OVERWRITES (not appends) `X-Forwarded-For`
|
||||
# with the real client IP. If XFF is trusted from an untrusted source, any
|
||||
# per-IP throttling — including the /mcp Basic brute-force limiter — can be
|
||||
# bypassed by an attacker who simply spoofs `X-Forwarded-For` to rotate IPs.
|
||||
# (The /mcp limiter keeps a global per-email key as an IP-independent backstop,
|
||||
# but the per-IP and per-IP+email keys rely on a trustworthy X-Forwarded-For.)
|
||||
#
|
||||
# TRUST_PROXY controls which proxies are trusted to set X-Forwarded-For.
|
||||
# Default (unset/empty): `loopback, linklocal, uniquelocal` — XFF is trusted
|
||||
# ONLY from private/loopback proxies, so a public-IP client cannot spoof req.ip.
|
||||
# This is the safe default for the common case where the reverse proxy runs on
|
||||
# loopback or a private network; req.ip still resolves to the real client.
|
||||
# WARNING: this changed the previous default of trust-all. If your reverse proxy
|
||||
# sits on a PUBLIC IP, the default will NOT trust its XFF and req.ip will be the
|
||||
# proxy's IP — set TRUST_PROXY accordingly. Accepted values:
|
||||
# - true restore trust-all (ONLY safe if a trusted proxy ALWAYS overwrites
|
||||
# X-Forwarded-For; otherwise clients can spoof their IP)
|
||||
# - false never trust X-Forwarded-For (req.ip is the socket peer)
|
||||
# - <int> number of trusted proxy hops in front of the app
|
||||
# - <list> comma-separated CIDR/IP list of trusted proxies, e.g.
|
||||
# `127.0.0.1, 10.0.0.0/8`
|
||||
# TRUST_PROXY=
|
||||
|
||||
# minimum of 32 characters. Generate one with: openssl rand -hex 32
|
||||
APP_SECRET=REPLACE_WITH_LONG_SECRET
|
||||
|
||||
Reference in New Issue
Block a user