Skip to content

middleware: test ClientIPFromHeader does not fall back to other headers - #1208

Open
renanmpimentel wants to merge 1 commit into
go-chi:masterfrom
renanmpimentel:test/client-ip-from-header-no-fallback
Open

renanmpimentel wants to merge 1 commit into
go-chi:masterfrom
renanmpimentel:test/client-ip-from-header-no-fallback

Conversation

@renanmpimentel

Copy link
Copy Markdown

What changed

Adds a subtest to TestAdvisory_GHSA_rjr7_jggh_pgcp, no_fallback_when_opted_in_header_missing. It covers a request where the header chosen for ClientIPFromHeader (X-Real-IP) is absent while attacker-controlled True-Client-IP and X-Forwarded-For are present, and asserts that no client IP is stored.

Why

only_opted_in_header_is_read documents that ClientIPFromHeader reads only the opted-in header and ignores True-Client-IP / X-Forwarded-For. But that test always sends the opted-in header too, so it can't tell "reads only that header" apart from "reads that header first, then falls back to the others". The fallback is exactly the RealIP behaviour behind the advisories this middleware replaces.

None of the existing tests cover the missing-header case together with spoofable headers either:

  • TestClientIPFromHeader/empty sends no headers at all.
  • TestClientIPLastWriteWins/earlier_persists_when_later_finds_nothing sends an XFF that resolves to the same IP either way.

As a result, either of these regressions in ClientIPFromHeader keeps go test ./middleware green:

// A: fall back to RealIP's header lookup
} else if rip := realIP(r); rip != "" {
	if ip, ok := parseHeaderAddr(rip); ok {
		r = r.WithContext(context.WithValue(r.Context(), clientIPCtxKey, ip))
	}
}
// B: fall back to X-Forwarded-For
values := r.Header.Values(header)
if len(values) == 0 {
	values = r.Header.Values("X-Forwarded-For")
}

Verification

  • go test -race -count=1 . ./middleware: ok (run twice)
  • go vet ./..., gofmt -l ., goimports -l .: clean
Suite Correct code Regression A Regression B
Before this PR ok ok (undetected) ok (undetected)
After this PR ok FAIL: VULNERABLE: fell back to attacker-supplied header, got "1.1.1.1" FAIL: ... got "2.2.2.2"

I found this while experimenting with Supertest, a tool for evaluating test effectiveness using mutation testing and test-harness mutilation.

This branch has not been deployed

No deployments
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