Bucket IPv6 rate-limit keys by /64 (closes #125)
All checks were successful
check / check (push) Successful in 3m5s
All checks were successful
check / check (push) Successful in 3m5s
Rate-limit keys were per-address, i.e. per /128 for IPv6. A routed /64 is the normal residential and mobile allocation, so a client rotated source addresses inside its own prefix and minted a fresh bucket per request — evading every limiter at the network layer, with no spoofing and nothing to detect. #88 closed the header half of this control; this is the network half. IPv6 now keys on the /64, IPv4 on the full address, via stdlib net/netip. IPv4-mapped form is unmapped rather than masked, so clients behind a mapping proxy do not collapse into one bucket. Independently reviewed twice. The first round found the trusted-proxy forwarded path — the one carrying production traffic — had no coverage at all, so a silent revert there was undetectable; that is now pinned. The reviewer confirmed both branches are independently mutation-tested: reverting either the direct-peer return or the forwarded return alone fails only that branch's tests. The 18-site test-constant refactor was verified byte-identical against next, with no pre-existing assertion changed. Known remaining coverage gap, judged not a defect: the fallback when the peer is trusted but the forwarded address does not parse has no test. Only operator-controlled addresses inside TRUSTED_PROXIES reach it, they already share the proxy's single bucket, and masking there can only merge operator proxies — fail-closed, nothing attacker-controlled.
This commit was merged in pull request #162.
This commit is contained in:
@@ -1001,7 +1001,14 @@ Every limiter here — receiver, login, and password change — identifies
|
||||
the client the same way, through one shared key function: the
|
||||
connection's own address, unless the peer is listed in
|
||||
`TRUSTED_PROXIES`, in which case the forwarded client address is used
|
||||
instead. See [Trusted proxies](#trusted-proxies). Deployed without that
|
||||
instead. That address becomes a bucket by family: IPv4 keys on the full
|
||||
address, IPv6 on its `/64` prefix. A routed `/64` is the normal
|
||||
residential and mobile IPv6 allocation, so keying IPv6 per address would
|
||||
let one subscriber rotate source addresses and mint a fresh bucket per
|
||||
request, evading these limits at the network layer without spoofing
|
||||
anything; the cost is that distinct clients inside one `/64` share a
|
||||
bucket. IPv4-mapped addresses (`::ffff:1.2.3.4`) key as the IPv4 address
|
||||
they carry. See [Trusted proxies](#trusted-proxies). Deployed without that
|
||||
variable set, a client behind a reverse proxy shares one bucket with
|
||||
every other client behind the same proxy. Set `TRUSTED_PROXIES` to the
|
||||
proxy's address to get per-client limits back. What the shared bucket
|
||||
|
||||
Reference in New Issue
Block a user