REDHAT-BUG-2536961: High severity Netty io.netty/netty-codec-http2 vulnerability
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
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Configuration
If you do not want to support Extended CONNECT through Netty's HTTP-object API, fail closed by requiring users to handle HTTP/2 or HTTP/3 Extended CONNECT at the native frame/header layer instead of allowing conversion by HttpConversionUtil.toHttpRequest(...).
Netty HTTP/2-3 HTTP-object conversion path (HttpConversionUtil.toHttpRequest(...)) Extended CONNECT handling policy = fail closed / reject Extended CONNECT unless native frame/header layer is used - Compensating control
In gateway/proxy/application-server/protocol-bridging deployments, ensure routing/authorization logic is not bypassed due to HTTP-object conversion collapsing Extended CONNECT into ordinary HTTP/1.1 CONNECT; apply authorization based on native HTTP/2/HTTP/3 frame/header semantics (e.g., preserve/inspect Extended-CONNECT `:protocol` and `:path`) rather than relying on the converted HTTP-object shape.
Event History
Frequently Asked Questions
Which applications are exposed to this issue?
Applications that accept HTTP/2 or HTTP/3 Extended CONNECT requests and pass them through Netty's HTTP-object conversion path to downstream handlers are exposed. The risk is greatest where downstream authorization or routing decisions need to distinguish Extended CONNECT from an ordinary CONNECT tunnel.
What does an attacker need to send to trigger the incorrect conversion?
An attacker needs to send an HTTP/2 or HTTP/3 Extended CONNECT request. Such a request uses CONNECT with a :protocol pseudo-header and a :path, but the converted HTTP/1.1-style object retains neither value.
What information is lost before a downstream Netty HTTP-object handler sees the request?
The :protocol value and the Extended-CONNECT :path are removed. The downstream handler instead sees an ordinary CONNECT request targeting the request authority, with only an x-http2-scheme or x-http3-scheme header indicating the original scheme.