fix(auth): harden middleware and auto-ban against IP spoofing and map growth #124

Closed
David wants to merge 1 commit from david/fix/harden-auth-middleware-ip-spoofing-and-ban-map into main AGit
Owner

Trust X-Forwarded-For / X-Real-IP only from a configured trusted-proxy CIDR. extract_client_ip now keys off the real socket peer address and honours forwarding headers solely when that peer falls inside Config::trusted_proxies (new TRUSTED_PROXY_CIDR env, comma-separated CIDRs, empty by default). This closes the spoofing vector that let any client evade the auto-ban or ban a victim by forging its IP. With no Config in app data (unit tests) the socket address is always used.

Route at+jwt case-insensitively: token_is_atjwt now compares typ with eq_ignore_ascii_case, so a mixed-case At+JWT reaches the EdDSA verifier instead of silently falling back to HS256.

Bound the auto-ban map: is_banned evicts expired entries on read (read-lock fast path, write-lock eviction only when an entry is expired, re-checked under the write lock) rather than waiting for the 5-minute sweep.

Clear dead middleware structures: remove the write-only AuthenticatedClaims request-extensions insert (never extracted) and the unused HttpMessage import, drop the write-only StrikeEntry.last_path field, and narrow SuspiciousPatterns to pub(crate) (no external callers).

Make the auto-ban test real: test_record_strike_triggers_ban now drives record_strike to the threshold over a lazy (never-connecting) pool and asserts ban promotion plus is_banned, replacing the placeholder. Adds a case-insensitive token_is_atjwt test.

#BUNYIP-72

Trust X-Forwarded-For / X-Real-IP only from a configured trusted-proxy CIDR. extract_client_ip now keys off the real socket peer address and honours forwarding headers solely when that peer falls inside Config::trusted_proxies (new TRUSTED_PROXY_CIDR env, comma-separated CIDRs, empty by default). This closes the spoofing vector that let any client evade the auto-ban or ban a victim by forging its IP. With no Config in app data (unit tests) the socket address is always used. Route at+jwt case-insensitively: token_is_atjwt now compares typ with eq_ignore_ascii_case, so a mixed-case At+JWT reaches the EdDSA verifier instead of silently falling back to HS256. Bound the auto-ban map: is_banned evicts expired entries on read (read-lock fast path, write-lock eviction only when an entry is expired, re-checked under the write lock) rather than waiting for the 5-minute sweep. Clear dead middleware structures: remove the write-only AuthenticatedClaims request-extensions insert (never extracted) and the unused HttpMessage import, drop the write-only StrikeEntry.last_path field, and narrow SuspiciousPatterns to pub(crate) (no external callers). Make the auto-ban test real: test_record_strike_triggers_ban now drives record_strike to the threshold over a lazy (never-connecting) pool and asserts ban promotion plus is_banned, replacing the placeholder. Adds a case-insensitive token_is_atjwt test. #BUNYIP-72
fix(auth): harden middleware and auto-ban against IP spoofing and map growth
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 1m0s
7667340159
Trust X-Forwarded-For / X-Real-IP only from a configured trusted-proxy CIDR. extract_client_ip now keys off the real socket peer address and honours forwarding headers solely when that peer falls inside Config::trusted_proxies (new TRUSTED_PROXY_CIDR env, comma-separated CIDRs, empty by default). This closes the spoofing vector that let any client evade the auto-ban or ban a victim by forging its IP. With no Config in app data (unit tests) the socket address is always used.

Route at+jwt case-insensitively: token_is_atjwt now compares typ with eq_ignore_ascii_case, so a mixed-case At+JWT reaches the EdDSA verifier instead of silently falling back to HS256.

Bound the auto-ban map: is_banned evicts expired entries on read (read-lock fast path, write-lock eviction only when an entry is expired, re-checked under the write lock) rather than waiting for the 5-minute sweep.

Clear dead middleware structures: remove the write-only AuthenticatedClaims request-extensions insert (never extracted) and the unused HttpMessage import, drop the write-only StrikeEntry.last_path field, and narrow SuspiciousPatterns to pub(crate) (no external callers).

Make the auto-ban test real: test_record_strike_triggers_ban now drives record_strike to the threshold over a lazy (never-connecting) pool and asserts ban promotion plus is_banned, replacing the placeholder. Adds a case-insensitive token_is_atjwt test.

#BUNYIP-72
David closed this pull request 2026-06-12 11:59:46 +02:00
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 1m0s

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
psa-systems/bunyip!124
No description provided.