changeset 9240:f3df785649ae

Request body: limited chunk extensions and trailer headers. Previously, arbitrary amounts of chunk extensions and trailer headers were accepted and skipped. Despite being under limit_conn / limit_req limits (if configured), this can be a DoS vector, so it is now limited by the client_max_body_size limit. Reported by Bartek Nowotarski.
author Maxim Dounin <mdounin@mdounin.ru>
date Sat, 30 Mar 2024 05:09:35 +0300
parents b2e16e8639c8
children 07ca679842de
files src/http/ngx_http.h src/http/ngx_http_parse.c src/http/ngx_http_request_body.c
diffstat 3 files changed, 35 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/http/ngx_http.h	Sat Mar 30 05:09:12 2024 +0300
+++ b/src/http/ngx_http.h	Sat Mar 30 05:09:35 2024 +0300
@@ -65,6 +65,7 @@
     ngx_uint_t           state;
     off_t                size;
     off_t                length;
+    off_t                skipped;
 };
 
 
--- a/src/http/ngx_http_parse.c	Sat Mar 30 05:09:12 2024 +0300
+++ b/src/http/ngx_http_parse.c	Sat Mar 30 05:09:35 2024 +0300
@@ -2257,6 +2257,9 @@
                 break;
             case LF:
                 state = sw_chunk_data;
+                break;
+            default:
+                ctx->skipped++;
             }
             break;
 
@@ -2298,6 +2301,9 @@
                 break;
             case LF:
                 state = sw_trailer;
+                break;
+            default:
+                ctx->skipped++;
             }
             break;
 
@@ -2333,6 +2339,9 @@
                 break;
             case LF:
                 state = sw_trailer;
+                break;
+            default:
+                ctx->skipped++;
             }
             break;
 
--- a/src/http/ngx_http_request_body.c	Sat Mar 30 05:09:12 2024 +0300
+++ b/src/http/ngx_http_request_body.c	Sat Mar 30 05:09:35 2024 +0300
@@ -1141,6 +1141,17 @@
                 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
                 if (clcf->client_max_body_size
+                    && clcf->client_max_body_size < rb->chunked->skipped)
+                {
+                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                  "client sent too many chunk extensions");
+
+                    r->lingering_close = 1;
+
+                    return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+                }
+
+                if (clcf->client_max_body_size
                     && clcf->client_max_body_size
                        - r->headers_in.content_length_n < rb->chunked->size)
                 {
@@ -1241,6 +1252,20 @@
 
             if (rc == NGX_AGAIN) {
 
+                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+                if (clcf->client_max_body_size
+                    && clcf->client_max_body_size < rb->chunked->skipped)
+                {
+                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                  "client sent too many chunk extensions "
+                                  "or trailer headers");
+
+                    r->lingering_close = 1;
+
+                    return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+                }
+
                 /* set rb->rest, amount of data we want to see next time */
 
                 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);