Conversation
The CONNECT stage authorizes a destination — the CONNECT target — but the MITM path only checks that the inner request's SNI matches its Host, and then dials that Host. A client can CONNECT to an allowed authority, complete the tunnel, and send an inner request whose SNI and Host both name a different one. The SNI==Host check passes, and the proxy MITMs and dials a host the CONNECT stage never saw. Where allowed and blocked hosts sit behind the same front end that routes on Host (shared CDN, virtual hosting) this bypasses a CONNECT-stage allowlist; RFC 9110 §7.4 calls out exactly this. Require the inner Host to name the same origin as the CONNECT target, compared by host (case-insensitive) and effective port, so "example.com" over https equals "example.com:443" and a bracketed IPv6 literal without a port equals its with-port form. Combined with the existing SNI check this gives CONNECT target == SNI == Host. Repeated requests to the same authority over one tunnel (HTTP/1.1 keep-alive, HTTP/2 multiplexing) are unaffected; reusing a tunnel for a different origin (HTTP/2 coalescing, RFC 9113 §9.1.1) is refused with 400, like the SNI/Host mismatch next to it.
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.
What
In MITM mode, refuse an inner request whose
Hostdoes not name the same origin as the tunnel's CONNECT target. Comparison is by host (case-insensitive) and effective port, soHost: example.commatchesCONNECT example.com:443, and[2001:db8::1]matches[2001:db8::1]:443.Why
The CONNECT stage authorizes a destination, but
handleHTTPonly checks SNI == Host and then dialsr.Host. So a client can:CONNECT allowed.example.com:443— passes the allowlist / external policy at the CONNECT stage;blocked.example.com, sendHost: blocked.example.com.SNI and Host agree with each other, so the existing check passes and the proxy mints a leaf for, and dials,
blocked.example.com— a host the CONNECT stage never saw. When allowed and blocked hosts share a front end that routes onHost(CDN, virtual hosting) this is a clean bypass of any CONNECT-stage policy; RFC 9110 §7.4 names this case. Together with the SNI check, the new check givesCONNECT target == SNI == Host, so the proxy only ever MITMs and dials the host the CONNECT stage authorized.Behavior notes
tunnelInfo != nil); direct HTTP proxying is unchanged.Hostis refused — that is the virtual-host case above. SOCKS5 clients should resolve remotely (socks5h://,--socks5-hostname), which the README already shows.Testing
TestSameAuthority— table over default/explicit/non-default ports, case, and IPv6 literals.TestTunnel_CONNECT_RejectsHostMismatch— end-to-end through the tunnel listener: CONNECT toallowed.example.com:443, TLS with SNI/Hostother.example.com→ 400, with a transport that fails the test if any upstream dial is attempted. The matching case (CONNECThost:443, innerHostwithout port) is already exercised byTestTunnel_CONNECT_HTTPS_MITM.go test -race ./internal/proxy/,go vet,golangci-lint runclean.