HTTP/2 and HTTP/3 Extended CONNECT requests are downgraded as regular CONNECT requests
A public GitHub Security Advisory (GHSA-w6j8-x45j-w75f) describes the following issue:
Summary
Netty's HTTP/2 and HTTP/3 HTTP-object conversion paths collapse Extended CONNECT requests into ordinary HTTP/1.1 CONNECT requests.
An Extended CONNECT request such as:
text :method: CONNECT :protocol: websocket :scheme: https :path: /admin/ws :authority: ws.example:443
is converted to an HTTP/1.1 object shaped like:
text CONNECT ws.example:443 HTTP/1.1 host: ws.example:443 x-http2-scheme: https
or, for HTTP/3:
text CONNECT ws.example:443 HTTP/1.1 host: ws.example:443 x-http3-scheme: https
The converted request no longer carries the :protocol value or the Extended-CONNECT :path. A downstream handler using Netty's HTTP-object API therefore cannot distinguish this request from a regular CONNECT tunnel to ws.example:443.
That is a semantic security boundary. RFC 8441 defines Extended CONNECT as a different CONNECT mode: the :protocol pseudo-header selects the protocol for the stream, :path remains part of the target URI, and the server must not treat :authority as the host to tunnel to in the same way it would for a regular CONNECT request. RFC 9220 applies the same pseudo-header and setting semantics to HTTP/3.
Technical Details
HTTP/2:
- codec-http2/.../HttpConversionUtil.java:272-280 uses :authority as the HTTP/1 request URI for every CONNECT request. - codec-http2/.../HttpConversionUtil.java:328-337 creates a DefaultHttpRequest with that URI, then translates headers. - codec-http2/.../HttpConversionUtil.java:806-813 maps :authority to Host and :scheme to x-http2-scheme, but does not preserve request :path; unrecognized pseudo-headers such as :protocol are dropped.
HTTP/3:
- codec-http3/.../Http3HeadersSink.java:79-94 recognizes Extended CONNECT and requires :method, :scheme, :authority, :path, and :protocol. - codec-http3/.../HttpConversionUtil.java:201-209 uses :authority as the HTTP/1 request URI for every CONNECT request. - codec-http3/.../HttpConversionUtil.java:259-268 creates a DefaultHttpRequest with that URI, then translates headers. - codec-http3/.../HttpConversionUtil.java:578-585 maps :authority to Host and :scheme to x-http3-scheme, but does not preserve request :path; :protocol is dropped as a pseudo-header.
The resulting object-level shape is security-relevant because applications and gateway integrations commonly authorize CONNECT differently from WebSocket, WebTransport, MASQUE, or other Extended CONNECT protocols. With Netty's conversion path, a policy that permits regular CONNECT to an allowlisted authority can be applied to an Extended CONNECT request whose actual protocol and path were erased before application code sees it.
Impact
A remote HTTP/2 or HTTP/3 client can send an Extended CONNECT request that Netty presents to downstream HTTP-object handlers as a regular CONNECT tunnel.
In gateway, proxy, application-server, or protocol-bridging deployments that use Netty's HTTP-object conversion path, this can bypass routing or authorization logic that is meant to distinguish:
- regular CONNECT tunnels from WebSocket/WebTransport/MASQUE-style Extended CONNECT streams; - allowed CONNECT authorities from protected Extended-CONNECT paths; - enabled/registered Extended CONNECT protocols from ordinary CONNECT traffic.
This report does not claim memory corruption, code execution, confidentiality impact, or impact on applications that never route HTTP/2 or HTTP/3 requests through Netty's HTTP-object adapters. The demonstrated impact is integrity loss through protocol-state confusion before application policy runs.
Suggested Fix
Recommended fix direction:
- do not convert Extended CONNECT into an ordinary HTTP/1 CONNECT object without preserving the Extended-CONNECT state; - either reject Extended CONNECT in HttpConversionUtil.toHttpRequest(...) unless Netty can expose the state safely, or preserve it with explicit extension headers such as x-http2-protocol / x-http3-protocol and x-http2-path / x-http3-path for request conversion; - keep regular CONNECT behavior unchanged; - add regression tests covering direct conversion and the frame-to-object adapters for both HTTP/2 and HTTP/3; - add controls proving regular CONNECT and Extended CONNECT no longer produce the same HTTP-object shape.
If maintainers prefer not to support Extended CONNECT through the HTTP-object API, the safest fix is to fail closed and require users to handle it at the native HTTP/2 or HTTP/3 frame/header layer.
References
- Netty security policy: https://github.com/netty/netty/security/policy - RFC 8441, Extended CONNECT: https://datatracker.ietf.org/doc/html/rfc8441#section-4 - RFC 9220, WebSockets over HTTP/3: https://datatracker.ietf.org/doc/html/rfc9220#section-3 - RFC 9113, HTTP/2 request pseudo-header fields: https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1 - RFC 9114, HTTP/3 request pseudo-header fields and CONNECT: https://datatracker.ietf.org/doc/html/rfc9114#section-4.3.1 - Go HTTP/2 Extended CONNECT implementation note requiring negotiation before use: https://go.dev/src/net/http/h2bundle.go - Go issue discussing Extended CONNECT API concerns: https://github.com/golang/go/issues/27244
Affected Products
- io.netty:netty-codec-http2 - io.netty:netty-codec-http3
Confirmed affected:
- current 4.2 branch at 7bae566a93e69409697fe57fa807910ba5c9720e - netty-4.2.15.Final at a41f7b289ce1d697c50846f3ade3983e22b2ed40 - netty-4.2.2.Final at 660edeaefa for HTTP/2 - netty-4.2.0.Final at 09e64d259c99 for HTTP/2 - netty-4.1.135.Final at f05f765d81460799c53123a207f665bf3b465171 for HTTP/2
Suggested affected ranges:
- io.netty:netty-codec-http2 >= 4.1.64.Final, <= 4.1.135.Final - io.netty:netty-codec-http2 >= 4.2.0.Final, <= 4.2.15.Final - io.netty:netty-codec-http3 >= 4.2.8.Final, <= 4.2.15.Final
The HTTP/2 lower
[truncated]
Affected: - maven:io.netty:netty-codec-http2 affected >= 4.1.64.Final, <= 4.1.137.Final; fixed unknown - maven:io.netty:netty-codec-http2 affected >= 4.2.0.Final, <= 4.2.17.Final; fixed unknown - maven:io.netty:netty-codec-http3 affected >= 4.2.8.Final, <= 4.2.15.Final; fixed unknown
Fixed versions: see advisory
Advisory: https://github.com/netty/netty/security/advisories/GHSA-w6j8-x45j-w75f