Skip to content

fix(session): walk every X-Forwarded-For header line for the client IP - #132

Merged
allen0099 merged 1 commit into
masterfrom
fix/forwarded-for-all-lines
Sep 25, 2026
Merged

allen0099 merged 1 commit into
masterfrom
fix/forwarded-for-all-lines

Conversation

@allen0099

Copy link
Copy Markdown
Owner

Closes #104.

Problem

get_client_ip() used headers.get("x-forwarded-for"), which returns only the first header line. Some proxies add their own X-Forwarded-For line instead of appending to the caller's. The caller's line then stayed first, the rightmost-untrusted walk ran over a value the caller chose, and a forged address could satisfy ip_binding.

Changes

  • get_client_ip() joins every X-Forwarded-For line with , before walking. RFC 9110 §5.3 treats repeated lines as one comma-separated value.
  • docs/SESSION.md notes that multiple lines are read as one chain.
  • CHANGELOG [Unreleased] Fixed entry.

Tests

  • New test_forwarded_chain_spans_every_header_line: a caller-sent first line plus a proxy-added second line must resolve to the proxy's entry. It fails on the old code and passes with the fix.
  • Four tests in tests/session/test_middleware.py built request.headers from a plain dict, which has no getlist(). They now use Starlette's Headers, which is what Request.headers actually is.

Local results:

  • ruff check/format: clean.
  • mypy (package strict, tests, scripts): clean.
  • zensical build --strict: passes.
  • Full suite with CACHEX_REQUIRE_LIVE_SERVERS=1 against Redis and Memcached: 717 passed, 100% coverage.

get_client_ip() read only the first X-Forwarded-For line. A proxy that
adds its own line instead of appending to the caller's left a
caller-chosen first line in charge of the rightmost-untrusted walk, so a
forged address could satisfy ip_binding. All lines are now joined into
one chain, as RFC 9110 treats them.

The middleware tests built request.headers from a plain dict, which has
no getlist(); the forwarded-header ones now use Starlette's Headers.

Closes #104
@allen0099
allen0099 force-pushed the fix/forwarded-for-all-lines branch from d077d44 to b091390 Compare September 25, 2026 10:56
@allen0099
allen0099 merged commit 4c05fa2 into master Sep 25, 2026
10 checks passed
@allen0099
allen0099 deleted the fix/forwarded-for-all-lines branch September 26, 2026 11:49
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.

get_client_ip reads only the first X-Forwarded-For header line

1 participant