Skip to content

fix(ip): handle scoped IPv6 proxy addresses in XFF - #3195

Open
jipeng6036-del wants to merge 4 commits into
labstack:masterfrom
jipeng6036-del:fix/3179-scoped-ipv6-xff
Open

jipeng6036-del wants to merge 4 commits into
labstack:masterfrom
jipeng6036-del:fix/3179-scoped-ipv6-xff

Conversation

@jipeng6036-del

Copy link
Copy Markdown

Trusted IPv6 proxies can supply an address with a zone, such as fe80::1%eth0. ExtractIPFromXFFHeader currently rejects that address and returns the proxy instead of continuing through the forwarded chain. Parse the scoped address strictly before dropping its zone, preserve untrusted peers unchanged, and share that validation with scheme extraction.

Based on #3180, retaining Bharat Kaurav's three commits and their source attribution. This version adds eight TCP HTTP regressions through Context.RealIP / Context.Scheme, including explicit trust ranges, repeated XFF fields and malformed inputs. It also avoids an extra formatting allocation after validation.

Fixes #3179.

Validation:

  • Original fork CI: all nine Go 1.25/1.26/1.27 × Ubuntu/macOS/Windows race/coverage jobs passed; golint, staticcheck and govulncheck passed with the unchanged workflows.
  • Independent module-ZIP consumers, without replace: eight scenarios passed on Windows and Linux (-race); current master fails three and passes five controls under the same conditions.
  • The HTTP tests use an explicit proxy RemoteAddr fixture. They exercise real TCP requests through Echo, without claiming OS link-local routing. Local Windows IPv6 permission failures and benchmark limitations are documented in the evidence.

Reproduction, exact source/artifact hashes and validation logs

Prepared and validated with Codex.

TheNetherWatcher and others added 4 commits October 29, 2026 19:18
Use netip.ParseAddr so an IPv4 address with a "%" suffix (e.g.
127.0.0.2%garbage) is still rejected. Return the direct IP unchanged
when the peer itself is untrusted, so RealIP() is the same with or
without an X-Forwarded-For header.

(cherry picked from commit db18e4b)
Preserve strict IPv6 parsing and slice the validated address before its zone. Add public RealIP and Scheme coverage through actual TCP HTTP requests, including explicit trust ranges and malformed forwarding inputs.

@agammann agammann left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the trust decision through the new helper and XFF loop. A valid scoped IPv6 address is parsed before its zone is removed for CIDR checks; malformed scopes and IPv4 zone-like suffixes still fail parsing, and an untrusted direct peer keeps its original zone-bearing address. The HTTP regression cases and all three package test suites pass on this head (go test ./... -count=1). The HTTP fixture substitutes RemoteAddr, so this does not establish live link-local routing behavior.

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.

ExtractIPFromXFFHeader ignores X-Forwarded-For when the peer address has an IPv6 zone

3 participants