This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 012dba7096 Validate HTTP2-Settings before accepting h2c upgrade (#1122)
012dba7096 is described below
commit 012dba7096fd18db91d1447389550e4883c889c0
Author: Arturo Bernal <[email protected]>
AuthorDate: Fri Nov 6 15:49:47 2026 +0100
Validate HTTP2-Settings before accepting h2c upgrade (#1122)
RFC 7540, section 3.2.1 requires a server to refuse the h2c upgrade if
the HTTP2-Settings header field value is not a valid BASE64URL-encoded
HTTP/2 SETTINGS payload. The server was deferring validation until after
the 101 response had been sent, which is a violation of the MUST NOT
constraint.
The fix validates the header in accept(): if the value cannot be decoded
as base64url, or if the decoded length is not a multiple of 6 octets
(each SETTINGS entry defined in section 6.5 is exactly 6 octets), the
upgrade is declined and the request continues as HTTP/1.1.
---
java/org/apache/coyote/http2/Http2Protocol.java | 16 ++++++++++++-
.../apache/coyote/http2/TestHttp2Section_3_2.java | 27 ++++++++++++++++++++++
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/coyote/http2/Http2Protocol.java
b/java/org/apache/coyote/http2/Http2Protocol.java
index 8fbd7ff40e..68cb06f8e1 100644
--- a/java/org/apache/coyote/http2/Http2Protocol.java
+++ b/java/org/apache/coyote/http2/Http2Protocol.java
@@ -18,6 +18,7 @@ package org.apache.coyote.http2;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
@@ -193,14 +194,27 @@ public class Http2Protocol implements UpgradeProtocol {
// Should only be one HTTP2-Settings header
Enumeration<String> settings =
request.getMimeHeaders().values("HTTP2-Settings");
int count = 0;
+ String settingsValue = null;
while (settings.hasMoreElements()) {
count++;
- settings.nextElement();
+ settingsValue = settings.nextElement();
}
if (count != 1) {
return false;
}
+ // RFC 7540, section 3.2.1: the server MUST NOT upgrade if the value
is not a valid
+ // BASE64URL-encoded HTTP/2 SETTINGS payload (section 6.5). A valid
payload decodes
+ // without error and its length is a multiple of 6 octets (each entry
is 6 octets).
+ try {
+ byte[] decoded = Base64.getUrlDecoder().decode(settingsValue);
+ if (decoded.length % 6 != 0) {
+ return false;
+ }
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
Enumeration<String> connection =
request.getMimeHeaders().values("Connection");
boolean found = false;
while (connection.hasMoreElements() && !found) {
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_3_2.java
b/test/org/apache/coyote/http2/TestHttp2Section_3_2.java
index 5532f2e547..72a8bf13db 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_3_2.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_3_2.java
@@ -188,4 +188,31 @@ public class TestHttp2Section_3_2 extends Http2TestBase {
// No need to test how trailing '=' are handled here. HTTP2Settings
payloads
// are always a multiple of 6 long which means valid payloads never end in
// '='. Invalid payloads will be rejected anyway.
+
+ @Test
+ public void testInvalidBase64Http2Settings() throws Exception {
+ // RFC 7540, section 3.2.1: a server MUST NOT upgrade if the
HTTP2-Settings value is
+ // not a valid BASE64URL-encoded SETTINGS payload. A value containing
characters
+ // outside the base64url alphabet must be rejected before the 101 is
sent.
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ doHttpUpgrade(DEFAULT_CONNECTION_HEADER_VALUE, "h2c", "HTTP2-Settings:
!!!invalid!!!\r\n", false);
+ parseHttp11Response();
+ }
+
+
+ @Test
+ public void testInvalidLengthHttp2Settings() throws Exception {
+ // RFC 7540, section 3.2.1: the HTTP2-Settings value must be a
BASE64URL encoding of
+ // an HTTP/2 SETTINGS payload (section 6.5). Each entry is exactly 6
octets, so the
+ // decoded length must be a multiple of 6. A valid base64url string
that decodes to a
+ // length that is not a multiple of 6 must also be rejected.
+ enableHttp2();
+ configureAndStartWebApplication();
+ openClientConnection();
+ // "AQID" is valid base64url and decodes to 3 bytes, which is not a
multiple of 6
+ doHttpUpgrade(DEFAULT_CONNECTION_HEADER_VALUE, "h2c", "HTTP2-Settings:
AQID\r\n", false);
+ parseHttp11Response();
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]