]> git.kaiwu.me - nginx.git/commitdiff
Restrict connection-specific headers in HTTP/2 and HTTP/3
authorRoman Arutyunyan <arut@nginx.com>
Wed, 8 Apr 2026 13:19:24 +0000 (17:19 +0400)
committerRoman Arutyunyan <arutyunyan.roman@gmail.com>
Tue, 14 Apr 2026 05:53:13 +0000 (09:53 +0400)
As per RFC 9113 and RFC 9114, any message containing such headers MUST be
treated as malformed.

As per RFC 9110, Section 7.6.1, the following headers are considered
connection-specific:

- Connection
- Proxy-Connection
- Keep-Alive
- TE
- Transfer-Encoding
- Upgrade

The only exception is the TE header field, which MAY be present in a
request header, but it MUST NOT contain any value other than "trailers".

src/http/ngx_http_request.c
src/http/v2/ngx_http_v2.c
src/http/v3/ngx_http_v3_request.c

index a9573a620777520ad42676e2372367a3d077cac5..a5cd44dcc9e4094f7b8400cf72fd2af3b7845f7e 100644 (file)
@@ -26,6 +26,8 @@ static ngx_int_t ngx_http_process_host(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_proxy_connection(ngx_http_request_t *r,
+    ngx_table_elt_t *h, ngx_uint_t offset);
 static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset);
 
@@ -82,6 +84,9 @@ ngx_http_header_t  ngx_http_headers_in[] = {
     { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),
                  ngx_http_process_connection },
 
+    { ngx_string("Proxy-Connection"), 0,
+                 ngx_http_process_proxy_connection },
+
     { ngx_string("If-Modified-Since"),
                  offsetof(ngx_http_headers_in_t, if_modified_since),
                  ngx_http_process_unique_header_line },
@@ -1928,6 +1933,21 @@ ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
 }
 
 
+static ngx_int_t
+ngx_http_process_proxy_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
+    ngx_uint_t offset)
+{
+    if (r->http_version >= NGX_HTTP_VERSION_20) {
+        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+                      "client sent \"Proxy-Connection\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
 static ngx_int_t
 ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
index efe22903fa907a7e613f437b0eba8a79be4cb9df..336718bad5446242baa0a8eef78be5ff17fac59c 100644 (file)
@@ -3820,6 +3820,45 @@ ngx_http_v2_run_request(ngx_http_request_t *r)
 
     r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
 
+    if (r->headers_in.connection) {
+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+                      "client sent \"Connection\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        goto failed;
+    }
+
+    if (r->headers_in.keep_alive) {
+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+                      "client sent \"Keep-Alive\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        goto failed;
+    }
+
+    if (r->headers_in.transfer_encoding) {
+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+                      "client sent \"Transfer-Encoding\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        goto failed;
+    }
+
+    if (r->headers_in.upgrade) {
+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+                      "client sent \"Upgrade\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        goto failed;
+    }
+
+    if (r->headers_in.te
+        && (r->headers_in.te->value.len != 8
+            || ngx_strncasecmp(r->headers_in.te->value.data,
+                               (u_char *) "trailers", 8) != 0))
+    {
+        ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+                      "client sent invalid \"TE\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        goto failed;
+    }
+
     if (r->headers_in.server.len == 0) {
         ngx_log_error(NGX_LOG_INFO, fc->log, 0,
                       "client sent neither \":authority\" nor \"Host\" header");
index 7bb61311d95e97138687d11fad9cf92cef44ff22..3b0fdbe9889baad61ce4df7af8a9203cb8a12d0b 100644 (file)
@@ -1021,6 +1021,45 @@ ngx_http_v3_process_request_header(ngx_http_request_t *r)
 
     c = r->connection;
 
+    if (r->headers_in.connection) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client sent \"Connection\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
+    }
+
+    if (r->headers_in.keep_alive) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client sent \"Keep-Alive\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
+    }
+
+    if (r->headers_in.transfer_encoding) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client sent \"Transfer-Encoding\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
+    }
+
+    if (r->headers_in.upgrade) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client sent \"Upgrade\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
+    }
+
+    if (r->headers_in.te
+        && (r->headers_in.te->value.len != 8
+            || ngx_strncasecmp(r->headers_in.te->value.data,
+                               (u_char *) "trailers", 8) != 0))
+    {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                      "client sent invalid \"TE\" header");
+        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+        return NGX_ERROR;
+    }
+
     if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) {
         return NGX_ERROR;
     }