CVE-2026-6790
ADVISORY - githubSummary
Summary
Jetty currently accepts HTTP/2 and HTTP/3 requests where the regular Host header and the pseudo-header :authority do not match. As a result, the same request can carry two different host identities through Jetty:
- logic based on
HttpURI/Request.getServerName(request)uses:authority - logic based on raw request headers continues to use
Host
This creates a host/authority confusion condition that can break security assumptions in higher layers.
Jetty already performs an explicit authority/Host consistency check on the HTTP/1.1 path, but equivalent validation is missing on the HTTP/2 and HTTP/3 paths.
Security Impact
This issue is not inherently remote code execution, but it can become security-relevant in deployments that rely on the request host for security-sensitive decisions, including:
- host-based access control
- virtual host isolation
- multi-tenant routing by hostname
- login/logout/callback URL construction
- reverse proxy and forwarded-header trust chains
- auditing, cache keys, and absolute URL generation
Potential consequences include:
- bypass of host-based ACLs
- virtual host or tenant isolation failures
- incorrect or attacker-influenced redirect/callback targets
- inconsistent proxy/downstream interpretation of the original target host
- misleading logs and audit records
Technical Root Cause
- On the HTTP/2 and HTTP/3 metadata builder paths:
:authorityis parsed separately into authority/URI stateHostis preserved as a normal request header- the two values are not compared for consistency
- On the HTTP/2 and HTTP/3 server entry paths:
- Jetty calls
ComplianceUtils.verify(httpCompliance, requestMetaData, listener) - this verification does not enforce
MISMATCHED_AUTHORITY
- On the HTTP/1.1 path:
- Jetty explicitly checks whether authority and
Hostmatch - mismatches are rejected by default
Relevant Code Locations
HTTP/2 metadata builder:
jetty-core/jetty-http2/jetty-http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/internal/MetaDataBuilder.java
HTTP/3 metadata builder:
jetty-core/jetty-http3/jetty-http3-qpack/src/main/java/org/eclipse/jetty/http3/qpack/internal/metadata/MetaDataBuilder.java
HTTP/2 server entry:
jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
HTTP/3 server entry:
jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
Shared HTTP compliance verification:
jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ComplianceUtils.java
HTTP/1.1 authority/Host consistency check:
jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java
Defined but not enforced on H2/H3:
jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java- violation: MISMATCHED_AUTHORITY
Reproduction
I reproduced this on local Jetty 12.1.9-SNAPSHOT source.
Minimal reproduction steps:
- Start a Jetty HTTP/2 or HTTP/3 test server.
- Send a request with:
- :authority = localhost:
- Host = evil.example:
- In the request handler, inspect both:
- Request.getServerName(request)
- request.getHeaders().get(HttpHeader.HOST)
- Observe whether Jetty rejects the request or allows both values to remain visible.
Observed result:
- HTTP/2: request is accepted and returns 200
- HTTP/3: request is accepted and returns 200
- the server can observe both:
- serverName=localhost
- hostHeader=evil.example:
This shows that a single attacker-controlled request can preserve two conflicting host interpretations inside Jetty.
Tests Used
HTTP/2 rejection test:
org.eclipse.jetty.http2.tests.HTTP2Test#testRejectMismatchedHostHeaderAndAuthority
HTTP/2 exploitability test:
org.eclipse.jetty.http2.tests.HTTP2Test#testMismatchedHostHeaderAndAuthoritySplitsAuthorityFromHostHeader
HTTP/3 rejection test:
org.eclipse.jetty.http3.tests.HandlerClientServerTest#testRejectMismatchedHostHeaderAndAuthority
HTTP/3 exploitability test:
org.eclipse.jetty.http3.tests.HandlerClientServerTest#testMismatchedHostHeaderAndAuthoritySplitsAuthorityFromHostHeader
Observed behavior:
- both rejection tests fail because Jetty returns 200 instead of 400
- both exploitability tests pass, confirming that Jetty exposes different host values to different layers
Project-Internal Evidence of Real Impact
Examples:
jetty-openidusesRequest.getServerName(request)to construct redirect URLsjetty-ee11-proxyuses the rawHostheader when buildingForwarded
This indicates that the issue is not merely theoretical: Jetty’s own ecosystem already contains code paths where different host sources are used for different purposes.
Affected Version
Confirmed affected version:
- 12.1.9-SNAPSHOT
Other versions may also be affected if they share the same HTTP/2 / HTTP/3 request construction and compliance-validation logic. I have not yet completed a historical version matrix and would recommend confirming exact affected ranges from Jetty’s branch history.
Suggested Fix
Recommend adding HTTP/2 and HTTP/3 validation equivalent to the existing HTTP/1.1 authority/Host consistency check:
- if both :authority and regular Host are present
- normalize and compare them
- if they do not match, reject the request with 400 Bad Request
- route the failure through the existing MISMATCHED_AUTHORITY compliance mechanism
Also adding explicit HTTP/2 and HTTP/3 regression coverage for this case.
Disclosure Status
- not publicly disclosed
- no public issue filed
- shared only privately with the Jetty security contacts
Common Weakness Enumeration (CWE)
Improper Input Validation
GitHub
3.9
CVSS SCORE
5.3mediumDebian
-
Ubuntu
-
CVSS SCORE
N/AmediumChainguard
CGA-245x-6qhg-5f4h
-
minimos
MINI-2mxw-mr6h-mxrr
-
minimos
MINI-2vqx-hfq6-3hrf
-
minimos
MINI-3768-675p-766g
-
minimos
MINI-423g-79rf-4xj2
-
minimos
MINI-43ph-688w-4fp9
-
minimos
MINI-5443-vfw2-2x3w
-
minimos
MINI-59jj-wggv-93rw
-
minimos
MINI-5f32-2gh7-3cv8
-
minimos
MINI-5rjg-3g55-43hq
-
minimos
MINI-63rp-cgr4-jvx4
-
minimos
MINI-746j-qv3x-8qp7
-
minimos
MINI-7pxf-7929-gwwp
-
minimos
MINI-82vg-wp48-334v
-
minimos
MINI-88x3-r3qv-gc72
-
minimos
MINI-98j3-6fcx-c3xp
-
minimos
MINI-9jhx-p8g9-38g2
-
minimos
MINI-9mrv-8w3v-ph3q
-
minimos
MINI-9w28-vg6x-fp8v
-
minimos
MINI-cj4q-qg8x-g7rf
-
minimos
MINI-cj5g-3455-ppmh
-
minimos
MINI-cr7m-4xxr-xfcj
-
minimos
MINI-cvc7-x5x5-2m3p
-
minimos
MINI-cwhf-f5qh-gwm3
-
minimos
MINI-g74g-mhq5-cm8w
-
minimos
MINI-gjg4-fvhm-mwfx
-
minimos
MINI-hhr3-wgvp-563v
-
minimos
MINI-hvr3-2c64-p342
-
minimos
MINI-hvww-7343-f86g
-
minimos
MINI-jfrg-q9gx-3xp9
-
minimos
MINI-jxp4-9wj9-qj88
-
minimos
MINI-mgwr-cpwf-fj8f
-
minimos
MINI-mv3x-3394-2g4q
-
minimos
MINI-mwh8-24w6-rm59
-
minimos
MINI-mxg2-5fqf-7c2q
-
minimos
MINI-q3wc-2jgp-g376
-
minimos
MINI-q8p7-h5hx-vfxp
-
minimos
MINI-qm28-x43g-cv28
-
minimos
MINI-r2cg-9mfc-fhf8
-
minimos
MINI-rjxf-m767-p3xw
-
minimos
MINI-v2f7-cwq2-f6qv
-
minimos
MINI-vp5j-m82m-r893
-
minimos
MINI-vv2j-297p-677h
-
minimos
MINI-w5vg-j65c-6779
-
minimos
MINI-w84h-frjr-cxqg
-
minimos
MINI-wjq3-x3p7-rjx2
-
minimos
MINI-wq9p-qh22-x65h
-
minimos
MINI-wxcr-mxhj-c5cm
-
minimos
MINI-x5vw-9mgw-g5hg
-
minimos
MINI-x7rw-gfh6-xwrw
-
minimos
MINI-xc8x-f5pg-2vx9
-