Skip to content

Charge the rate limit to the client, and stop the map growing without bound - #144

Merged
Shine-neko merged 1 commit into
mainfrom
fix/rate-limit-bypassed-by-a-forged-header
Aug 23, 2026
Merged

Charge the rate limit to the client, and stop the map growing without bound#144
Shine-neko merged 1 commit into
mainfrom
fix/rate-limit-bypassed-by-a-forged-header

Conversation

@Shine-neko

Copy link
Copy Markdown
Contributor

The limiter keyed its buckets on the leftmost X-Forwarded-For entry, taken straight off the request:

.get("x-forwarded-for").and_then(|v| v.split(',').next())

That entry is written by the client. A fresh forged value per request bought a fresh bucket per request, so the limiter never fired on the traffic it exists to hold back — and resolve_client_ip was right there, already used by ip_allow_list, which only believes the header when the peer is a trusted proxy. The limiter now goes through it, so a header from an untrusted peer is ignored and one from a proxy we trust is still honoured.

Every forged value also left a permanent entry behind. cleanup existed to prevent exactly that, carried a comment saying so, and was marked #[allow(dead_code)] — nothing called it. check now sweeps once the map crosses 10k sources: idle buckets first, and if a flood of fresh addresses leaves nothing idle, the buckets closest to full. A source at full tokens has spent nothing, so forgetting it grants it nothing; a depleted bucket — the one that is being held back — outlives it.

The tests fail against the old behaviour, showing two forged headers resolving to two identities, and 15000 sources tracked against a 10000 cap.

Fifth of the findings from a full audit, after #140 to #143.

@Shine-neko
Shine-neko merged commit 5f63952 into main Aug 23, 2026
3 checks passed
@Shine-neko
Shine-neko deleted the fix/rate-limit-bypassed-by-a-forged-header branch August 23, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant