]> git.kaiwu.me - nginx.git/commitdiff
HTTP/3: restricted symbols in header names.
authorRoman Arutyunyan <arut@nginx.com>
Tue, 19 May 2020 12:34:00 +0000 (15:34 +0300)
committerRoman Arutyunyan <arut@nginx.com>
Tue, 19 May 2020 12:34:00 +0000 (15:34 +0300)
As per HTTP/3 draft 27, a request or response containing uppercase header
field names MUST be treated as malformed.  Also, existing rules applied
when parsing HTTP/1 header names are also applied to HTTP/3 header names:

- null character is not allowed
- underscore character may or may not be treated as invalid depending on the
  value of "underscores_in_headers"
- all non-alphanumeric characters with the exception of '-' are treated as
  invalid

Also, the r->locase_header field is now filled while parsing an HTTP/3
header.

Error logging for invalid headers is fixed as well.

src/http/ngx_http_request.c
src/http/v3/ngx_http_v3.h
src/http/v3/ngx_http_v3_request.c

index e77c4bc35784eca11ffd785eaa31c1c67321226d..e3d217f799edf52c0b3fe482dba058c7ec05b5c4 100644 (file)
@@ -1511,7 +1511,8 @@ ngx_http_process_request_headers(ngx_event_t *rev)
         switch (r->http_version) {
 #if (NGX_HTTP_V3)
         case NGX_HTTP_VERSION_30:
-            rc = ngx_http_v3_parse_header(r, r->header_in);
+            rc = ngx_http_v3_parse_header(r, r->header_in,
+                                          cscf->underscores_in_headers);
             break;
 #endif
 
@@ -1530,9 +1531,10 @@ ngx_http_process_request_headers(ngx_event_t *rev)
                 /* there was error while a header line parsing */
 
                 ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                              "client sent invalid header line: \"%*s\"",
-                              r->header_end - r->header_name_start,
-                              r->header_name_start);
+                              "client sent invalid header line: \"%*s: %*s\"",
+                              r->header_name_end - r->header_name_start,
+                              r->header_name_start,
+                              r->header_end - r->header_start, r->header_start);
                 continue;
             }
 
index f80b74f3aec8c8592aa25836158a23deb968caf7..29cc06ee95090ab38476815820c42b52e556804b 100644 (file)
@@ -68,7 +68,8 @@ typedef struct {
 
 
 ngx_int_t ngx_http_v3_parse_request(ngx_http_request_t *r, ngx_buf_t *b);
-ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b);
+ngx_int_t ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b,
+    ngx_uint_t allow_underscores);
 ngx_int_t ngx_http_v3_parse_request_body(ngx_http_request_t *r, ngx_buf_t *b,
     ngx_http_chunked_t *ctx);
 ngx_chain_t *ngx_http_v3_create_header(ngx_http_request_t *r);
index 2bb6274890fbd975987a39bc286c334c49d77695..59b8ce5b81765b56821865fb1f701a51b622cd57 100644 (file)
@@ -116,16 +116,23 @@ failed:
 
 
 ngx_int_t
-ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b)
+ngx_http_v3_parse_header(ngx_http_request_t *r, ngx_buf_t *b,
+    ngx_uint_t allow_underscores)
 {
+    u_char                        ch;
     ngx_int_t                     rc;
     ngx_str_t                    *name, *value;
+    ngx_uint_t                    hash, i, n;
     ngx_connection_t             *c;
     ngx_http_v3_parse_headers_t  *st;
 
     c = r->connection;
     st = r->h3_parse;
 
+    if (st->header_rep.state == 0) {
+        r->invalid_header = 0;
+    }
+
     if (st->state == 0) {
         if (r->header_name_start == NULL) {
             name = &st->header_rep.header.name;
@@ -164,9 +171,45 @@ done:
     r->header_name_end = name->data + name->len;
     r->header_start = value->data;
     r->header_end = value->data + value->len;
-    r->header_hash = ngx_hash_key(name->data, name->len);
 
-    /* XXX r->lowcase_index = i; */
+    hash = 0;
+    i = 0;
+
+    for (n = 0; n < name->len; n++) {
+        ch = name->data[n];
+
+        if (ch >= 'A' && ch <= 'Z') {
+            /*
+             * A request or response containing uppercase
+             * header field names MUST be treated as malformed
+             */
+            return NGX_HTTP_PARSE_INVALID_HEADER;
+        }
+
+        if (ch == '\0') {
+            return NGX_HTTP_PARSE_INVALID_HEADER;
+        }
+
+        if (ch == '_' && !allow_underscores) {
+            r->invalid_header = 1;
+            continue;
+        }
+
+        if ((ch < 'a' || ch > 'z')
+            && (ch < '0' || ch > '9')
+            && ch != '-' && ch != '_')
+        {
+            r->invalid_header = 1;
+            continue;
+        }
+
+        hash = ngx_hash(hash, ch);
+        r->lowcase_header[i++] = ch;
+        i &= (NGX_HTTP_LC_HEADER_LEN - 1);
+    }
+
+    r->header_hash = hash;
+    r->lowcase_index = i;
 
     return NGX_OK;
 }