feat: add automatic DNS rebinding protection for localhost servers #760
+185
−0
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
Add DNS rebinding protection that is automatically enabled when requests arrive via localhost (127.0.0.1, [::1]). This protects against malicious websites using DNS rebinding to interact with local MCP servers.
(note: this was a claude assisted PR, mostly wanted to see how difficult passing this test would be for this SDK)
Design Goal: Secure by Default
The primary goal is to make it difficult to run a localhost server without these protections by mistake. There are other approaches that could provide secure defaults (e.g., a helper for
ListenAndServethat does the localhost check), but usinghttp.LocalAddrContextKeyfor runtime detection seemed like the most backwards compatible approach and least likely to be disabled by accident.With this implementation:
DisableLocalhostProtection: trueChanges
DisableLocalhostProtectionoption toStreamableHTTPOptionsisLocalhostAddrandisLocalhostHosthelper functionsServeHTTP, rejecting non-localhost Host headers with 403 ForbiddenHow it works
The protection uses
http.LocalAddrContextKeyto detect the connection's local address at runtime. When a request arrives via localhost (127.0.0.1 or [::1]), the handler validates that the Host header also matches a localhost value. If not, the request is rejected with 403 Forbidden.This approach means:
127.0.0.1or0.0.0.0Edge case: Reverse proxies
If a reverse proxy (e.g., Envoy, nginx) runs on the same host and forwards requests to the MCP server via localhost while preserving the original Host header, those requests would be rejected. In this case, users should either:
DisableLocalhostProtection: trueTesting
isLocalhostAddrandisLocalhostHosthelper functionslocalhost-host-rebinding-rejected: PASSlocalhost-host-valid-accepted: PASSRelated
localhostHostValidation()middlewaredns-rebinding-protectionscenario