feat(http): Host header validation with :authority fallback and rejection logging#798
Merged
joshrotenberg merged 1 commit intomainfrom May 7, 2026
Merged
Conversation
…tion logging Adds defense-in-depth Host header validation alongside our existing Origin checks. Origin protects browsers from cross-site rebinding; Host rejects requests whose Host header doesn't match the server's expected hostname, blocking direct DNS-rebinding attacks where a malicious site resolves its own domain to 127.0.0.1. API additions on HttpTransport (and delegated on UnixSocketTransport): - disable_host_validation() - allowed_hosts(Vec<String>) Behavior: - Localhost host values (localhost / 127.0.0.1 / ::1, with any port, including bracketed IPv6) always pass. - Empty allowed_hosts means "don't enforce" -- existing deployments see no behavior change. Operators opt in by passing an allowlist. - Missing Host header falls back to request.uri().authority() so the HTTP/2 :authority pseudo-header works even when middleware like axum::Router::nest strips Hyper's synthesized Host. - Rejections emit tracing::warn! with the offending value, enabling log-based alerting for rebinding attempts. Same logging added to the existing Origin rejection paths. Bundles three rmcp PRs (#764 host check, #826 logging, #827 :authority). Closes #796
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds defense-in-depth
Hostheader validation alongside our existingOriginchecks. Origin protects browsers from cross-site rebinding; Host rejects requests whoseHostheader doesn't match the server's expected hostname, blocking direct DNS-rebinding attacks where a malicious site resolves its own domain to127.0.0.1. All peer SDKs (TypeScript, Python, Go, Java, rmcp) ship both checks.Bundles three rmcp PRs:
:authorityfallback for when middleware likeaxum::Router::neststrips the synthesized Host header.tracing::warn!on rejections so operators can alert via logs.API additions
UnixSocketTransportdelegates the same two methods.Behavior
localhost/127.0.0.1/::1, with any port, including bracketed IPv6) always pass.allowed_hostsmeans "don't enforce" -- existing deployments see no behavior change. Operators opt in by passing an allowlist.Hostheader falls back torequest.uri().authority()so the HTTP/2:authoritypseudo-header works even when middleware strips Hyper's synthesizedHost.tracing::warn!with the offending value. Same logging added to the existing Origin rejection paths.POST /,GET /,DELETE /). The latter two now extract fromRequestinstead of justHeaderMapso they can read the URI authority.Tests
9 new tests in
transport::http::tests:test_is_localhost_host_variants-- helper unit test (localhost, IPv6, ports)test_host_validation_allows_localhosttest_host_validation_allows_configured_hosttest_host_validation_rejects_unconfigured_hosttest_host_validation_no_allowlist_accepts_any_host-- backward compattest_disabled_host_validation_allows_any_with_allowlist-- opt-outtest_effective_host_prefers_headertest_effective_host_falls_back_to_authoritytest_effective_host_returns_none_when_both_missing682/682 lib tests pass.
Closes #796.
Test plan
cargo fmt --all -- --checkcargo clippy --all-targets --all-features -- -D warningscargo test -p tower-mcp --lib --all-featurescargo test -p tower-mcp --test '*' --all-featurescargo doc --no-deps --all-features