From 1605a3a31688ef25ecd3098e9938534b7982842e Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Sun, 22 Feb 2026 20:55:07 +0100 Subject: [PATCH 01/13] docs: add request body limits explanation --- .../blog/2026-02-22-about-body-limitations.md | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 content/blog/2026-02-22-about-body-limitations.md diff --git a/content/blog/2026-02-22-about-body-limitations.md b/content/blog/2026-02-22-about-body-limitations.md new file mode 100644 index 0000000..b5eef91 --- /dev/null +++ b/content/blog/2026-02-22-about-body-limitations.md @@ -0,0 +1,263 @@ +--- +title: 'About body limitations' +date: '2026-02-22T00:00:00+02:00' +author: airween +--- + +Have you ever wondered what exactly the request body limits means in ModSecurity and how do they work? + + + +As you probably know, ModSecurity has two limits on the size of the request body: [SecRequestBodyLimit](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x) and [SecRequestBodyNoFilesLimit](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodynofileslimit). + +There is also a handler for a special case, what to do if the body size is larger than expected - [SecRequestBodyLimitAction](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimitaction). + +Two new PRs (for [v3](https://github.com/owasp-modsecurity/ModSecurity/pull/3476) and for [v2](https://github.com/owasp-modsecurity/ModSecurity/pull/3483)) have recently appeared on GH, from Hiroaki Nakamura ([@hnakamur](https://github.com/hnakamur)), where he tried to improve the behavior of these limits. + + +Under the PR [3483](https://github.com/owasp-modsecurity/ModSecurity/pull/3483) we discussed a lot about how could he make that better, and we are a bit stuck. + +I think it would be good to know what the community's expectations are for this feature, but first, let me explain how these restrictions work in reality. + +#### A really simple example + +I changed the engine a little to demonstrate the behavior - it always shows the size that exceeds the limit, and the limit itself. + +I think the first question is which constraint is "stronger", what the engine checks first. + +Consider we have a simple JSON file with length of 120 bytes: +``` +$ cat payloadmin4.json +[1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] + +$ ls -l payloadmin4.json +-rw-rw-r-- 1 airween airween 120 febr 15 19.45 payloadmin4.json +``` + +Now let's set the restrictions to extremely low to see what happens if I send the above file: +``` +SecRequestBodyLimit 115 +SecRequestBodyNoFilesLimit 110 +``` + +The `NoFiles` limit usually lower than the "single" one, and this is very important to understand why. We can see this later. + +Now let's send the request: +``` +$ curl -v -H "Content-Type: application/json" -X POST --data @payloadmin4.json http://localhost +... +> POST / HTTP/1.1 +> Host: localhost +> User-Agent: curl/8.18.0 +> Accept: */* +> Content-Type: application/json +> Content-Length: 120 +``` +and check the log: +``` +ModSecurity: Request body (Content-Length (120)) is larger than the configured limit (115). +``` + +As you can see the first limitation that engine checks is the `SecRequestBodyLimit`. If it's bigger than the value has set, the engine will blocks the request immediatelly. + +Now increase the `SecRequestBodyLimit` higher than the body size, and check it again: + +``` +SecRequestBodyLimit 130 +SecRequestBodyNoFilesLimit 110 +``` +Send the request again and check the log: +``` +ModSecurity: Request body no files data length (120) is larger than the configured limit (110). +``` +Now the no files limitation was exceeded - the value we set is 110, but the payload size is 120. + +**Conclusion**: The first variable that the engine checks is the `SecRequestBodyLimit`, and the second one is the `SecRequestBodyNoFilesLimit`. + +#### What's the difference between the two limitations? + +The `SecRequestBodyLimit` controls the **entire request body size**, no matter what's the request's `Content-Type`. + +The `SecRequestBodyNoFilesLimit` as the documentation [says](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodynofileslimit): +>_"Configures the maximum request body size ModSecurity will accept for buffering, excluding the size of any files being transported in the request."_ + +In other words: anything that is not a file to be uploaded. + +We have now come to understand why the limit on the NoFiles is lower than the total limit. If a user wants to upload a file, it can usually be much, much larger than a "simple" form request. + +#### Understanding the excluded size + +Okay, but what is the term _"excluding the size of any files being transported"_? + +If we send a JSON request, then it's not a file, the entire JSON payload is subject to the assessment of this directive - see where we set the `SecRequestBodyLimit` to 130, and the no files limit blocked the request. + +If we create a smaller file and try to send it, it works as we expect: + +``` +$ cat payloadmin2.json +[1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] + +$ ls -la payloadmin2.json +-rw-rw-r-- 1 airween airween 103 febr 15 19.58 payloadmin2.json +``` +Now we have a JSON file with 103 bytes. Send it: +``` +$ curl -v -H "Content-Type: application/json" -X POST --data @payloadmin2.json http://localhost +... +> POST / HTTP/1.1 +> Host: localhost +> User-Agent: curl/8.18.0 +> Accept: */* +> Content-Type: application/json +> Content-Length: 103 +``` + +Got 200, no issue, hooray. + +Note, that the behavior is the same if you send an XML or URL encoded requests. + +And now try to send the same file but as a file upload - for this we can use the multipart request, and the `-F` switch for `curl`: +``` +$ curl -v -F "upload=@payloadmin2.json" http://localhost +... +> POST / HTTP/1.1 +> Host: localhost +> User-Agent: curl/8.18.0 +> Accept: */* +> Content-Length: 325 +> Content-Type: multipart/form-data; boundary=------------------------yR5iNnu9lY48kNvLTbqOiH +``` + +The request size is 325 bytes, and we got: +``` +Request body no files data length (118) is larger than the configured limit (110) +``` + +Hmmm... what's 325 bytes, and what's 118 bytes there? The JSON file's size is 103 bytes. + +The 325 bytes is the size of the multipart request. In this type, the client splits the files into multiple parts and adds boundaries. This additional content increases the request size from 103 to 325 bytes, like this: +``` +--------------------------yR5iNnu9lY48kNvLTbqOiH +Content-Disposition: form-data; name="upload"; filename="payloadmin2.json" +Content-Type: application/octet-stream + +[1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] +--------------------------yR5iNnu9lY48kNvLTbqOiH-- + +``` +The length of this request (with EOL characters (CRLF!)) is 325 bytes in total. Without boundaries, we got this part: +``` +Content-Disposition: form-data; name="upload"; filename="payloadmin2.json" +Content-Type: application/octet-stream + +[1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] +``` +Also, here we need to count CRLF's in. The total size of this part is 221 bytes. It's still not 103 and not 108 bytes... But let's count the "additional" parts of the request: +``` +Content-Disposition: form-data; name="upload"; filename="payloadmin2.json"\r\n +``` +This is a 76 bytes long string. +``` +Content-Type: application/octet-stream\r\n +``` +Here the lengt is 40. And finally an empty line: +``` +\r\n +``` +where the length is 2. + +76 + 40 + 2 = 118. + +This is the "magic" part that the engine _excludes_ from the payload. If there are multiple files to upload, each file will have a section like this, and these additional sections will produce the content that the engine compares to the `SecRequestBodyNoFilesLimit` value. + +With the default settings, ModSecurity allows 12.5MB for `SecRequestBodyLimit` and 128kB for `SecRequestBodyNoFilesLimit` - see the [recommended](https://github.com/owasp-modsecurity/ModSecurity/blob/v2/master/modsecurity.conf-recommended#L45-L46) config file. This means: +* if the content type of the request is JSON, XML or URL encoded, then the `SecRequestBodyNoFilesLimit` (the lower) will be applied (even if the payload is extreme high, because this is much lower) +* if the content type is multipart, then the total size will be compared with `SecRequestBodyLimit` **AND** the excluded part with the `SecRequestBodyNoFilesLimit` values. + +**A very important note**: both configuration directives have a hard-coded limit in v2 engine, which is 1GB (see the documentation above). In v3, there is no hard-coded limit, and I think that's the normal. We will remove this limit from v2 soon. + +#### A misterious SecRequestBodyLimitAction directive + +As I mentioned above, ModSecurity has a directive to handle a special case, the `SecRequestBodyLimitAction`. The possible values are `Reject` (this is the default) or `ProcessPartial`. This describe the behavior, what the engine needs to do if the size is bigger than the configured one - in case of default values, what does it need to do if the payload size is greater than 12.5 MB. + +`Reject` is clear: it terminates the connection with status 413. + +`ProcessPartial` is a bit more sophisticated, because it processes the part which is under the limit, but does not care about the rest. + +Wait... the rest don't care? So does this mean that if someone sends a multipart request that is larger than allowed and the admin has set the engine to `ProcessPartial`, the rest of the checks will be skipped? + +Yes, yes. + +Let's see how does this work. + +I have three files: +``` +$ cat file1.json +[1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] + +$ cat file2.json +{"array.array_1": "1234567890123456", "array.array_2": "1234567890123456", "array.array_3": "1234567890123456", "array.array_4": "1234567890123456"} + +$ cat file3.json +["attack"] + +$ ls -la file1.json file2.json file3.json +-rw-rw-r-- 1 airween airween 148 febr 15 19.45 file1.json +-rw-rw-r-- 1 airween airween 120 febr 15 19.45 file2.json +-rw-rw-r-- 1 airween airween 11 febr 15 19.45 file3.json +``` + +Create a rule that checks the files' content: +``` +SecRule FILES_TMP_CONTENT "@rx attack" "id:192372,log,deny" +``` +and just for sure, increase the extreme lower value to a bit higher: +``` +SecRequestBodyLimit 400 +SecRequestBodyNoFilesLimit 350 +``` + +Now send the multipart request, but be sure that the file with content "attack" is the first (this means the file is under the limit): +``` +$ curl -v -F "upload1=@file3.json" -F "upload2=@file2.json" -F "upload3=file1.json" http://localhost +``` +Check the log: +``` +ModSecurity: Request body (Content-Length (671)) is larger than the configured limit (400). +... +ModSecurity: Warning. Pattern match "attack" at FILES_TMP_CONTENT:upload1. +``` +What we see here is that the engine warns us that the size is higher than the configured limit, but no worries, the admin set the limit actio to `ProjectPartial`, so it continues the investigation. Then it checks the first file (which contains the pattern "attack") and the rule is triggered. + +Let's change the order of the files: +``` +$ curl -v -F "upload1=@file1.json" -F "upload2=@file2.json" -F "upload3=file3.json" http://localhost +``` +and check the log: +``` +ModSecurity: Request body (Content-Length (780)) is larger than the configured limit (400). +``` +Ops... there is no triggered rule. + +This is what the `ProcessPartial` does. + +#### Why was this feature added? + +To understand the situation, please read the [documentation](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimitaction): + +>_By default, ModSecurity will reject a request body that is longer than specified. This is problematic especially when ModSecurity is being run in DetectionOnly mode and the intent is to be totally passive and not take any disruptive actions against the transaction. With the ability to choose what happens once a limit is reached, site administrators can choose to inspect only the first part of the request, the part that can fit into the desired limit, and let the rest through. This is not ideal from a possible evasion issue perspective, however it may be acceptable under certain circumstances._ + +#### Extend this behavior + +Back to PRs. The main concept is to extend this behavior to other payloads, such as JSON, XML, and URL-encoded data. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but there is another option to extend the behavior of the existing directive to XML/JSON and URL-encoded requests. + +There is no similar feature to avoid the 413 error for JSON/XML or URL-encoded requests. Even in `DetectionOnly` mode, if the engine reaches the `SecRequestBodyNoFilesLimit` limit, the client will receive a 413 error. + +This feature allowed clients to send larger payloads than allowed during the test period, and the administrator could collect logs. + + + + + + + From 5366c75b694e02de9b10d7702dccbd58badd898e Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Mon, 23 Feb 2026 10:02:18 +0100 Subject: [PATCH 02/13] Update content/blog/2026-02-22-about-body-limitations.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felipe Zipitría <3012076+fzipi@users.noreply.github.com> --- content/blog/2026-02-22-about-body-limitations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2026-02-22-about-body-limitations.md b/content/blog/2026-02-22-about-body-limitations.md index b5eef91..c415f01 100644 --- a/content/blog/2026-02-22-about-body-limitations.md +++ b/content/blog/2026-02-22-about-body-limitations.md @@ -4,7 +4,7 @@ date: '2026-02-22T00:00:00+02:00' author: airween --- -Have you ever wondered what exactly the request body limits means in ModSecurity and how do they work? +Have you ever wondered what exactly the request body limits mean in ModSecurity and how they work? From 67cfedd15513bb89c0053c267939be3bcebe133f Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Mon, 23 Feb 2026 10:21:24 +0100 Subject: [PATCH 03/13] Apply suggestions from @fzipi's code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felipe Zipitría <3012076+fzipi@users.noreply.github.com> --- .../blog/2026-02-22-about-body-limitations.md | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/content/blog/2026-02-22-about-body-limitations.md b/content/blog/2026-02-22-about-body-limitations.md index c415f01..fa851b7 100644 --- a/content/blog/2026-02-22-about-body-limitations.md +++ b/content/blog/2026-02-22-about-body-limitations.md @@ -8,7 +8,7 @@ Have you ever wondered what exactly the request body limits mean in ModSecurity -As you probably know, ModSecurity has two limits on the size of the request body: [SecRequestBodyLimit](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x) and [SecRequestBodyNoFilesLimit](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodynofileslimit). +As you probably know, ModSecurity has two limits on the size of the request body: [SecRequestBodyLimit](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimit) and [SecRequestBodyNoFilesLimit](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodynofileslimit). There is also a handler for a special case, what to do if the body size is larger than expected - [SecRequestBodyLimitAction](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimitaction). @@ -40,7 +40,7 @@ SecRequestBodyLimit 115 SecRequestBodyNoFilesLimit 110 ``` -The `NoFiles` limit usually lower than the "single" one, and this is very important to understand why. We can see this later. +The `NoFiles` limit is usually lower than the "single" one — we'll see why below. Now let's send the request: ``` @@ -58,9 +58,9 @@ and check the log: ModSecurity: Request body (Content-Length (120)) is larger than the configured limit (115). ``` -As you can see the first limitation that engine checks is the `SecRequestBodyLimit`. If it's bigger than the value has set, the engine will blocks the request immediatelly. +As you can see, the first limitation the engine checks is `SecRequestBodyLimit`. If the body is bigger than the configured value, the engine blocks the request immediately. -Now increase the `SecRequestBodyLimit` higher than the body size, and check it again: +Now set `SecRequestBodyLimit` higher than the body size and check again: ``` SecRequestBodyLimit 130 @@ -70,7 +70,7 @@ Send the request again and check the log: ``` ModSecurity: Request body no files data length (120) is larger than the configured limit (110). ``` -Now the no files limitation was exceeded - the value we set is 110, but the payload size is 120. +Now the no-files limitation was exceeded — we set the limit to 110, but the payload is 120 bytes. **Conclusion**: The first variable that the engine checks is the `SecRequestBodyLimit`, and the second one is the `SecRequestBodyNoFilesLimit`. @@ -83,13 +83,13 @@ The `SecRequestBodyNoFilesLimit` as the documentation [says](https://github.com/ In other words: anything that is not a file to be uploaded. -We have now come to understand why the limit on the NoFiles is lower than the total limit. If a user wants to upload a file, it can usually be much, much larger than a "simple" form request. +Now we can see why the NoFiles limit is lower than the total limit. File uploads are typically much larger than simple form submissions. #### Understanding the excluded size Okay, but what is the term _"excluding the size of any files being transported"_? -If we send a JSON request, then it's not a file, the entire JSON payload is subject to the assessment of this directive - see where we set the `SecRequestBodyLimit` to 130, and the no files limit blocked the request. +If we send a JSON request, it's not a file upload, so the entire JSON payload counts against this directive — recall when we set `SecRequestBodyLimit` to 130 and the no-files limit blocked the request. If we create a smaller file and try to send it, it works as we expect: @@ -114,7 +114,7 @@ $ curl -v -H "Content-Type: application/json" -X POST --data @payloadmin2.json h Got 200, no issue, hooray. -Note, that the behavior is the same if you send an XML or URL encoded requests. +Note that the behavior is the same if you send XML or URL-encoded requests. And now try to send the same file but as a file upload - for this we can use the multipart request, and the `-F` switch for `curl`: ``` @@ -133,7 +133,7 @@ The request size is 325 bytes, and we got: Request body no files data length (118) is larger than the configured limit (110) ``` -Hmmm... what's 325 bytes, and what's 118 bytes there? The JSON file's size is 103 bytes. +Hmmm... where do the 325 bytes and 118 bytes come from? The JSON file is only 103 bytes. The 325 bytes is the size of the multipart request. In this type, the client splits the files into multiple parts and adds boundaries. This additional content increases the request size from 103 to 325 bytes, like this: ``` @@ -145,14 +145,14 @@ Content-Type: application/octet-stream --------------------------yR5iNnu9lY48kNvLTbqOiH-- ``` -The length of this request (with EOL characters (CRLF!)) is 325 bytes in total. Without boundaries, we got this part: +The length of this request, including line endings (CRLF), is 325 bytes in total. Without boundaries, we have this part: ``` Content-Disposition: form-data; name="upload"; filename="payloadmin2.json" Content-Type: application/octet-stream [1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] ``` -Also, here we need to count CRLF's in. The total size of this part is 221 bytes. It's still not 103 and not 108 bytes... But let's count the "additional" parts of the request: +We also need to count the CRLFs. The total size of this part is 221 bytes — still not 103 or 118 bytes. Let's count the non-file parts of the request: ``` Content-Disposition: form-data; name="upload"; filename="payloadmin2.json"\r\n ``` @@ -160,7 +160,7 @@ This is a 76 bytes long string. ``` Content-Type: application/octet-stream\r\n ``` -Here the lengt is 40. And finally an empty line: +This is 40 bytes. And finally, an empty line: ``` \r\n ``` @@ -168,27 +168,27 @@ where the length is 2. 76 + 40 + 2 = 118. -This is the "magic" part that the engine _excludes_ from the payload. If there are multiple files to upload, each file will have a section like this, and these additional sections will produce the content that the engine compares to the `SecRequestBodyNoFilesLimit` value. +This is the "magic" part that the engine _excludes_ from the payload. If there are multiple files to upload, each file will have a section like this, and these sections make up the overhead that the engine compares against the `SecRequestBodyNoFilesLimit` value. With the default settings, ModSecurity allows 12.5MB for `SecRequestBodyLimit` and 128kB for `SecRequestBodyNoFilesLimit` - see the [recommended](https://github.com/owasp-modsecurity/ModSecurity/blob/v2/master/modsecurity.conf-recommended#L45-L46) config file. This means: -* if the content type of the request is JSON, XML or URL encoded, then the `SecRequestBodyNoFilesLimit` (the lower) will be applied (even if the payload is extreme high, because this is much lower) -* if the content type is multipart, then the total size will be compared with `SecRequestBodyLimit` **AND** the excluded part with the `SecRequestBodyNoFilesLimit` values. +* if the content type of the request is JSON, XML, or URL-encoded, then `SecRequestBodyNoFilesLimit` (the lower value) will be applied (even if the payload is extremely large, because this limit is much lower) +* if the content type is multipart, then the total size is checked against `SecRequestBodyLimit` **and** the non-file portion against `SecRequestBodyNoFilesLimit` -**A very important note**: both configuration directives have a hard-coded limit in v2 engine, which is 1GB (see the documentation above). In v3, there is no hard-coded limit, and I think that's the normal. We will remove this limit from v2 soon. +**A very important note**: both configuration directives have a hard-coded limit in the v2 engine, which is 1GB (see the documentation above). In v3, there is no hard-coded limit, which is the expected behavior. We will remove this limit from v2 soon. -#### A misterious SecRequestBodyLimitAction directive +#### A mysterious SecRequestBodyLimitAction directive -As I mentioned above, ModSecurity has a directive to handle a special case, the `SecRequestBodyLimitAction`. The possible values are `Reject` (this is the default) or `ProcessPartial`. This describe the behavior, what the engine needs to do if the size is bigger than the configured one - in case of default values, what does it need to do if the payload size is greater than 12.5 MB. +As I mentioned above, ModSecurity has a directive to handle this case: `SecRequestBodyLimitAction`. The possible values are `Reject` (the default) or `ProcessPartial`. This describes what the engine should do when the body exceeds the configured limit — with default values, what to do if the payload is greater than 12.5 MB. `Reject` is clear: it terminates the connection with status 413. -`ProcessPartial` is a bit more sophisticated, because it processes the part which is under the limit, but does not care about the rest. +`ProcessPartial` is more sophisticated: it processes data up to the limit and ignores the rest. -Wait... the rest don't care? So does this mean that if someone sends a multipart request that is larger than allowed and the admin has set the engine to `ProcessPartial`, the rest of the checks will be skipped? +Wait... the rest isn't inspected? So if someone sends a multipart request larger than allowed and the admin has set the engine to `ProcessPartial`, the remaining data won't be checked? Yes, yes. -Let's see how does this work. +Let's see how this works. I have three files: ``` @@ -219,7 +219,7 @@ SecRequestBodyNoFilesLimit 350 Now send the multipart request, but be sure that the file with content "attack" is the first (this means the file is under the limit): ``` -$ curl -v -F "upload1=@file3.json" -F "upload2=@file2.json" -F "upload3=file1.json" http://localhost +$ curl -v -F "upload1=@file3.json" -F "upload2=@file2.json" -F "upload3=@file1.json" http://localhost ``` Check the log: ``` @@ -227,7 +227,7 @@ ModSecurity: Request body (Content-Length (671)) is larger than the configured l ... ModSecurity: Warning. Pattern match "attack" at FILES_TMP_CONTENT:upload1. ``` -What we see here is that the engine warns us that the size is higher than the configured limit, but no worries, the admin set the limit actio to `ProjectPartial`, so it continues the investigation. Then it checks the first file (which contains the pattern "attack") and the rule is triggered. +What we see here is that the engine warns us that the size exceeds the configured limit, but since the admin set the limit action to `ProcessPartial`, it continues processing. It then inspects the first file (which contains the pattern "attack") and the rule fires. Let's change the order of the files: ``` @@ -237,7 +237,7 @@ and check the log: ``` ModSecurity: Request body (Content-Length (780)) is larger than the configured limit (400). ``` -Ops... there is no triggered rule. +Oops — the rule didn't fire. This is what the `ProcessPartial` does. @@ -249,11 +249,11 @@ To understand the situation, please read the [documentation](https://github.com/ #### Extend this behavior -Back to PRs. The main concept is to extend this behavior to other payloads, such as JSON, XML, and URL-encoded data. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but there is another option to extend the behavior of the existing directive to XML/JSON and URL-encoded requests. +Back to PRs. The main concept is to extend this behavior to other payloads, such as JSON, XML, and URL-encoded data. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but another option is to extend the existing directive's behavior to cover JSON/XML and URL-encoded requests. -There is no similar feature to avoid the 413 error for JSON/XML or URL-encoded requests. Even in `DetectionOnly` mode, if the engine reaches the `SecRequestBodyNoFilesLimit` limit, the client will receive a 413 error. +There is currently no way to avoid the 413 error for JSON/XML or URL-encoded requests. Even in `DetectionOnly` mode, if the engine reaches the `SecRequestBodyNoFilesLimit` limit, the client will receive a 413 error. -This feature allowed clients to send larger payloads than allowed during the test period, and the administrator could collect logs. +This would let clients send oversized payloads during a testing period while the administrator collects logs. From 5279ab773450c54fd9bcb2d2954eadf6299d1fe3 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Mon, 23 Feb 2026 13:06:30 +0100 Subject: [PATCH 04/13] Update content/blog/2026-02-22-about-body-limitations.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felipe Zipitría <3012076+fzipi@users.noreply.github.com> --- content/blog/2026-02-22-about-body-limitations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2026-02-22-about-body-limitations.md b/content/blog/2026-02-22-about-body-limitations.md index fa851b7..b6d68fe 100644 --- a/content/blog/2026-02-22-about-body-limitations.md +++ b/content/blog/2026-02-22-about-body-limitations.md @@ -1,5 +1,5 @@ --- -title: 'About body limitations' +title: 'How Big Is Too Big? A Deep Dive into ModSecurity Request Body Limits' date: '2026-02-22T00:00:00+02:00' author: airween --- From 5f05c516b2303c59d026992423f8b63951612157 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Mon, 23 Feb 2026 13:11:44 +0100 Subject: [PATCH 05/13] change blogpost content's name --- ...-too-big--a-deep-dive-into-modsecurity-request-body-limits.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename content/blog/{2026-02-22-about-body-limitations.md => 2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md} (100%) diff --git a/content/blog/2026-02-22-about-body-limitations.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md similarity index 100% rename from content/blog/2026-02-22-about-body-limitations.md rename to content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md From ac5f5a9eec06c45110e77a41c0896b674fbcb780 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Mon, 23 Feb 2026 13:22:28 +0100 Subject: [PATCH 06/13] chore: add syntax type to pre-formatted texts --- ...ve-into-modsecurity-request-body-limits.md | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md index b6d68fe..627abb4 100644 --- a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md +++ b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md @@ -26,7 +26,7 @@ I changed the engine a little to demonstrate the behavior - it always shows the I think the first question is which constraint is "stronger", what the engine checks first. Consider we have a simple JSON file with length of 120 bytes: -``` +```bash $ cat payloadmin4.json [1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] @@ -35,7 +35,7 @@ $ ls -l payloadmin4.json ``` Now let's set the restrictions to extremely low to see what happens if I send the above file: -``` +```apache SecRequestBodyLimit 115 SecRequestBodyNoFilesLimit 110 ``` @@ -43,7 +43,7 @@ SecRequestBodyNoFilesLimit 110 The `NoFiles` limit is usually lower than the "single" one — we'll see why below. Now let's send the request: -``` +```bash $ curl -v -H "Content-Type: application/json" -X POST --data @payloadmin4.json http://localhost ... > POST / HTTP/1.1 @@ -54,7 +54,7 @@ $ curl -v -H "Content-Type: application/json" -X POST --data @payloadmin4.json h > Content-Length: 120 ``` and check the log: -``` +```bash ModSecurity: Request body (Content-Length (120)) is larger than the configured limit (115). ``` @@ -62,12 +62,12 @@ As you can see, the first limitation the engine checks is `SecRequestBodyLimit`. Now set `SecRequestBodyLimit` higher than the body size and check again: -``` +```apache SecRequestBodyLimit 130 SecRequestBodyNoFilesLimit 110 ``` Send the request again and check the log: -``` +```bash ModSecurity: Request body no files data length (120) is larger than the configured limit (110). ``` Now the no-files limitation was exceeded — we set the limit to 110, but the payload is 120 bytes. @@ -93,7 +93,7 @@ If we send a JSON request, it's not a file upload, so the entire JSON payload co If we create a smaller file and try to send it, it works as we expect: -``` +```bash $ cat payloadmin2.json [1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] @@ -101,7 +101,7 @@ $ ls -la payloadmin2.json -rw-rw-r-- 1 airween airween 103 febr 15 19.58 payloadmin2.json ``` Now we have a JSON file with 103 bytes. Send it: -``` +```bash $ curl -v -H "Content-Type: application/json" -X POST --data @payloadmin2.json http://localhost ... > POST / HTTP/1.1 @@ -117,7 +117,7 @@ Got 200, no issue, hooray. Note that the behavior is the same if you send XML or URL-encoded requests. And now try to send the same file but as a file upload - for this we can use the multipart request, and the `-F` switch for `curl`: -``` +```bash $ curl -v -F "upload=@payloadmin2.json" http://localhost ... > POST / HTTP/1.1 @@ -129,14 +129,14 @@ $ curl -v -F "upload=@payloadmin2.json" http://localhost ``` The request size is 325 bytes, and we got: -``` +```apache Request body no files data length (118) is larger than the configured limit (110) ``` Hmmm... where do the 325 bytes and 118 bytes come from? The JSON file is only 103 bytes. The 325 bytes is the size of the multipart request. In this type, the client splits the files into multiple parts and adds boundaries. This additional content increases the request size from 103 to 325 bytes, like this: -``` +```plain --------------------------yR5iNnu9lY48kNvLTbqOiH Content-Disposition: form-data; name="upload"; filename="payloadmin2.json" Content-Type: application/octet-stream @@ -146,22 +146,22 @@ Content-Type: application/octet-stream ``` The length of this request, including line endings (CRLF), is 325 bytes in total. Without boundaries, we have this part: -``` +```plain Content-Disposition: form-data; name="upload"; filename="payloadmin2.json" Content-Type: application/octet-stream [1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] ``` We also need to count the CRLFs. The total size of this part is 221 bytes — still not 103 or 118 bytes. Let's count the non-file parts of the request: -``` +```plain Content-Disposition: form-data; name="upload"; filename="payloadmin2.json"\r\n ``` This is a 76 bytes long string. -``` +```plain Content-Type: application/octet-stream\r\n ``` This is 40 bytes. And finally, an empty line: -``` +```plain \r\n ``` where the length is 2. @@ -191,7 +191,7 @@ Yes, yes. Let's see how this works. I have three files: -``` +```bash $ cat file1.json [1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] @@ -208,21 +208,21 @@ $ ls -la file1.json file2.json file3.json ``` Create a rule that checks the files' content: -``` +```apache SecRule FILES_TMP_CONTENT "@rx attack" "id:192372,log,deny" ``` and just for sure, increase the extreme lower value to a bit higher: -``` +```apache SecRequestBodyLimit 400 SecRequestBodyNoFilesLimit 350 ``` Now send the multipart request, but be sure that the file with content "attack" is the first (this means the file is under the limit): -``` +```bash $ curl -v -F "upload1=@file3.json" -F "upload2=@file2.json" -F "upload3=@file1.json" http://localhost ``` Check the log: -``` +```bash ModSecurity: Request body (Content-Length (671)) is larger than the configured limit (400). ... ModSecurity: Warning. Pattern match "attack" at FILES_TMP_CONTENT:upload1. @@ -230,11 +230,11 @@ ModSecurity: Warning. Pattern match "attack" at FILES_TMP_CONTENT:upload1. What we see here is that the engine warns us that the size exceeds the configured limit, but since the admin set the limit action to `ProcessPartial`, it continues processing. It then inspects the first file (which contains the pattern "attack") and the rule fires. Let's change the order of the files: -``` +```bash $ curl -v -F "upload1=@file1.json" -F "upload2=@file2.json" -F "upload3=file3.json" http://localhost ``` and check the log: -``` +```bash ModSecurity: Request body (Content-Length (780)) is larger than the configured limit (400). ``` Oops — the rule didn't fire. @@ -255,9 +255,3 @@ There is currently no way to avoid the 413 error for JSON/XML or URL-encoded req This would let clients send oversized payloads during a testing period while the administrator collects logs. - - - - - - From bde64268dedb5a068b19985f0cf962ae1e6cf02a Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Tue, 24 Feb 2026 08:52:54 +0100 Subject: [PATCH 07/13] Apply suggestions from @theseions's code review Co-authored-by: Max Leske <250711+theseion@users.noreply.github.com> --- ...ve-into-modsecurity-request-body-limits.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md index 627abb4..e49fc27 100644 --- a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md +++ b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md @@ -10,22 +10,22 @@ Have you ever wondered what exactly the request body limits mean in ModSecurity As you probably know, ModSecurity has two limits on the size of the request body: [SecRequestBodyLimit](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimit) and [SecRequestBodyNoFilesLimit](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodynofileslimit). -There is also a handler for a special case, what to do if the body size is larger than expected - [SecRequestBodyLimitAction](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimitaction). +There is also a handler for the case, when the request body size is larger than expected - [SecRequestBodyLimitAction](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimitaction). Two new PRs (for [v3](https://github.com/owasp-modsecurity/ModSecurity/pull/3476) and for [v2](https://github.com/owasp-modsecurity/ModSecurity/pull/3483)) have recently appeared on GH, from Hiroaki Nakamura ([@hnakamur](https://github.com/hnakamur)), where he tried to improve the behavior of these limits. -Under the PR [3483](https://github.com/owasp-modsecurity/ModSecurity/pull/3483) we discussed a lot about how could he make that better, and we are a bit stuck. +We've had a long discussion ([3483](https://github.com/owasp-modsecurity/ModSecurity/pull/3483)) about how the current behaviour can be improved, and we are a bit stuck. I think it would be good to know what the community's expectations are for this feature, but first, let me explain how these restrictions work in reality. #### A really simple example -I changed the engine a little to demonstrate the behavior - it always shows the size that exceeds the limit, and the limit itself. +For the following, I've modified the engine a little to demonstrate the behaviour - it always shows the amount by which a limit has been exceeded, and the limit itself. -I think the first question is which constraint is "stronger", what the engine checks first. +I think the first question to examine is which constraint is "stronger", i.e., which limit does the engine check first. -Consider we have a simple JSON file with length of 120 bytes: +Consider a simple JSON file with a length of 120 bytes: ```bash $ cat payloadmin4.json [1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456,1234567890123456] @@ -34,13 +34,13 @@ $ ls -l payloadmin4.json -rw-rw-r-- 1 airween airween 120 febr 15 19.45 payloadmin4.json ``` -Now let's set the restrictions to extremely low to see what happens if I send the above file: +Now let's set the limits to very low values to see what happens when I send the above file: ```apache SecRequestBodyLimit 115 SecRequestBodyNoFilesLimit 110 ``` -The `NoFiles` limit is usually lower than the "single" one — we'll see why below. +`SecRequestBodyNoFilesLimit` limit is usually lower than `SecRequestBodyLimit` — we'll see why below. Now let's send the request: ```bash @@ -58,9 +58,9 @@ and check the log: ModSecurity: Request body (Content-Length (120)) is larger than the configured limit (115). ``` -As you can see, the first limitation the engine checks is `SecRequestBodyLimit`. If the body is bigger than the configured value, the engine blocks the request immediately. +As you can see, the first limit the engine checks is `SecRequestBodyLimit`. If the body is bigger than the configured value, the engine blocks the request immediately. -Now set `SecRequestBodyLimit` higher than the body size and check again: +Now we'll set `SecRequestBodyLimit` higher than `SecRequestBodyNoFilesLimit` and check again: ```apache SecRequestBodyLimit 130 @@ -74,7 +74,7 @@ Now the no-files limitation was exceeded — we set the limit to 110, but the pa **Conclusion**: The first variable that the engine checks is the `SecRequestBodyLimit`, and the second one is the `SecRequestBodyNoFilesLimit`. -#### What's the difference between the two limitations? +#### What's the difference between the two limits on the request body size? The `SecRequestBodyLimit` controls the **entire request body size**, no matter what's the request's `Content-Type`. From 349eba74b041d01a8eebdc4e49eadea70845e0b6 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Tue, 24 Feb 2026 09:20:13 +0100 Subject: [PATCH 08/13] docs: more updates based on @theseion's review --- ...g--a-deep-dive-into-modsecurity-request-body-limits.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md index e49fc27..80d202b 100644 --- a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md +++ b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md @@ -23,7 +23,7 @@ I think it would be good to know what the community's expectations are for this For the following, I've modified the engine a little to demonstrate the behaviour - it always shows the amount by which a limit has been exceeded, and the limit itself. -I think the first question to examine is which constraint is "stronger", i.e., which limit does the engine check first. +I think the first question to examine is which constraint is "stronger", i.e., which limit does the engine check first. Consider a simple JSON file with a length of 120 bytes: ```bash @@ -211,7 +211,7 @@ Create a rule that checks the files' content: ```apache SecRule FILES_TMP_CONTENT "@rx attack" "id:192372,log,deny" ``` -and just for sure, increase the extreme lower value to a bit higher: +and just in case, increase the extremely low limits a bit: ```apache SecRequestBodyLimit 400 SecRequestBodyNoFilesLimit 350 @@ -243,13 +243,13 @@ This is what the `ProcessPartial` does. #### Why was this feature added? -To understand the situation, please read the [documentation](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimitaction): +The [documentation](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodylimitaction) explains why `ProcessPartial` was added to the engine: >_By default, ModSecurity will reject a request body that is longer than specified. This is problematic especially when ModSecurity is being run in DetectionOnly mode and the intent is to be totally passive and not take any disruptive actions against the transaction. With the ability to choose what happens once a limit is reached, site administrators can choose to inspect only the first part of the request, the part that can fit into the desired limit, and let the rest through. This is not ideal from a possible evasion issue perspective, however it may be acceptable under certain circumstances._ #### Extend this behavior -Back to PRs. The main concept is to extend this behavior to other payloads, such as JSON, XML, and URL-encoded data. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but another option is to extend the existing directive's behavior to cover JSON/XML and URL-encoded requests. +Back to PRs. The main concept of the open PRs is to extend this behavior to other payloads, such as JSON, XML, and URL-encoded data. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but another option is to extend the existing directive's behavior to cover JSON/XML and URL-encoded requests. There is currently no way to avoid the 413 error for JSON/XML or URL-encoded requests. Even in `DetectionOnly` mode, if the engine reaches the `SecRequestBodyNoFilesLimit` limit, the client will receive a 413 error. From 7c95ecf4b6e489956b700e9f4bb800dbfc4d6d4e Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Thu, 26 Feb 2026 09:26:55 +0100 Subject: [PATCH 09/13] And more updates based on @theseion's review --- ...o-big--a-deep-dive-into-modsecurity-request-body-limits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md index 80d202b..095c886 100644 --- a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md +++ b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md @@ -249,9 +249,9 @@ The [documentation](https://github.com/owasp-modsecurity/ModSecurity/wiki/Refere #### Extend this behavior -Back to PRs. The main concept of the open PRs is to extend this behavior to other payloads, such as JSON, XML, and URL-encoded data. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but another option is to extend the existing directive's behavior to cover JSON/XML and URL-encoded requests. +Back to PRs. The main concept of the open PRs is align the "action" directives with the "limit" directives. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but another option is to extend the existing directive's behavior to handle the case where `SecRequestBodyNoFilesLimit` is exceeded. -There is currently no way to avoid the 413 error for JSON/XML or URL-encoded requests. Even in `DetectionOnly` mode, if the engine reaches the `SecRequestBodyNoFilesLimit` limit, the client will receive a 413 error. +Additionally, we have been discussing the issue that there is currently no way to avoid the 413 error for requests that trigger `SecRequestBodyNoFilesLimit`, even in `DetectionOnly` mode. This would let clients send oversized payloads during a testing period while the administrator collects logs. From d52fbfea74a88ab42f0b75b7cafd742eca69d1db Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Thu, 26 Feb 2026 09:32:13 +0100 Subject: [PATCH 10/13] Apply suggestions from code review Co-authored-by: Max Leske <250711+theseion@users.noreply.github.com> --- ...too-big--a-deep-dive-into-modsecurity-request-body-limits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md index 095c886..0b441cd 100644 --- a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md +++ b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md @@ -76,7 +76,7 @@ Now the no-files limitation was exceeded — we set the limit to 110, but the pa #### What's the difference between the two limits on the request body size? -The `SecRequestBodyLimit` controls the **entire request body size**, no matter what's the request's `Content-Type`. +The `SecRequestBodyLimit` controls the **entire request body size**, regardless of the request's `Content-Type` or `Transfer-Encoding`. The `SecRequestBodyNoFilesLimit` as the documentation [says](https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v2.x)#secrequestbodynofileslimit): >_"Configures the maximum request body size ModSecurity will accept for buffering, excluding the size of any files being transported in the request."_ From f2ab78292d6fe18893182b1a3559303570164a70 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Thu, 26 Feb 2026 09:34:06 +0100 Subject: [PATCH 11/13] Update section title Co-authored-by: Max Leske <250711+theseion@users.noreply.github.com> --- ...too-big--a-deep-dive-into-modsecurity-request-body-limits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md index 0b441cd..c5134b8 100644 --- a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md +++ b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md @@ -247,7 +247,7 @@ The [documentation](https://github.com/owasp-modsecurity/ModSecurity/wiki/Refere >_By default, ModSecurity will reject a request body that is longer than specified. This is problematic especially when ModSecurity is being run in DetectionOnly mode and the intent is to be totally passive and not take any disruptive actions against the transaction. With the ability to choose what happens once a limit is reached, site administrators can choose to inspect only the first part of the request, the part that can fit into the desired limit, and let the rest through. This is not ideal from a possible evasion issue perspective, however it may be acceptable under certain circumstances._ -#### Extend this behavior +#### Improved handler behavior Back to PRs. The main concept of the open PRs is align the "action" directives with the "limit" directives. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but another option is to extend the existing directive's behavior to handle the case where `SecRequestBodyNoFilesLimit` is exceeded. From 93b8ea9883ee15e4c92357515eab89ec471e2403 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Thu, 26 Feb 2026 20:00:36 +0100 Subject: [PATCH 12/13] Update content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md Co-authored-by: Max Leske <250711+theseion@users.noreply.github.com> --- ...too-big--a-deep-dive-into-modsecurity-request-body-limits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md index c5134b8..90edb88 100644 --- a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md +++ b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md @@ -249,7 +249,7 @@ The [documentation](https://github.com/owasp-modsecurity/ModSecurity/wiki/Refere #### Improved handler behavior -Back to PRs. The main concept of the open PRs is align the "action" directives with the "limit" directives. The proposed directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but another option is to extend the existing directive's behavior to handle the case where `SecRequestBodyNoFilesLimit` is exceeded. +Back to PRs. The main concept of the open PRs is to align the "action" directives with the "limit" directives. The proposed new directive is `SecRequestBodyNoFilesLimitAction` and would follow the behavior of `SecRequestBodyLimitAction`, but another option is to extend the existing directive's behavior to handle the case where `SecRequestBodyNoFilesLimit` is exceeded. Additionally, we have been discussing the issue that there is currently no way to avoid the 413 error for requests that trigger `SecRequestBodyNoFilesLimit`, even in `DetectionOnly` mode. From 0d02745dbcfd9c153dde31ed3d6c30cc25b4ecc0 Mon Sep 17 00:00:00 2001 From: Ervin Hegedus Date: Fri, 27 Feb 2026 10:45:36 +0100 Subject: [PATCH 13/13] Apply suggestions from @theseion's code review Co-authored-by: Max Leske <250711+theseion@users.noreply.github.com> --- ...ve-into-modsecurity-request-body-limits.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md index 90edb88..3b1d69a 100644 --- a/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md +++ b/content/blog/2026-02-22-how-big-is-too-big--a-deep-dive-into-modsecurity-request-body-limits.md @@ -87,11 +87,11 @@ Now we can see why the NoFiles limit is lower than the total limit. File uploads #### Understanding the excluded size -Okay, but what is the term _"excluding the size of any files being transported"_? +Okay, but what does _"excluding the size of any files being transported"_ mean exactly? -If we send a JSON request, it's not a file upload, so the entire JSON payload counts against this directive — recall when we set `SecRequestBodyLimit` to 130 and the no-files limit blocked the request. +When we send a JSON request, it's not a file upload, so the entire JSON payload counts against `SecRequestBodyNoFilesLimit` — recall when we set `SecRequestBodyLimit` to 130 and the no-files limit blocked the request. -If we create a smaller file and try to send it, it works as we expect: +If we create a smaller payload and try to send it, it works as we expect: ```bash $ cat payloadmin2.json @@ -116,7 +116,7 @@ Got 200, no issue, hooray. Note that the behavior is the same if you send XML or URL-encoded requests. -And now try to send the same file but as a file upload - for this we can use the multipart request, and the `-F` switch for `curl`: +And now try sending the same payload but as a file upload - for this we can use the multipart request, and the `-F` switch for `curl`: ```bash $ curl -v -F "upload=@payloadmin2.json" http://localhost ... @@ -133,7 +133,7 @@ The request size is 325 bytes, and we got: Request body no files data length (118) is larger than the configured limit (110) ``` -Hmmm... where do the 325 bytes and 118 bytes come from? The JSON file is only 103 bytes. +Hmmm... where did the 325 bytes and 118 bytes come from? The JSON file is only 103 bytes. The 325 bytes is the size of the multipart request. In this type, the client splits the files into multiple parts and adds boundaries. This additional content increases the request size from 103 to 325 bytes, like this: ```plain @@ -168,17 +168,17 @@ where the length is 2. 76 + 40 + 2 = 118. -This is the "magic" part that the engine _excludes_ from the payload. If there are multiple files to upload, each file will have a section like this, and these sections make up the overhead that the engine compares against the `SecRequestBodyNoFilesLimit` value. +As you can see, every byte that is not part of the file content counts against the `SecRequestBodyNoFilesLimit`. If there are multiple files to upload, each file will have a section like this, and these sections make up the overhead that the engine compares against the `SecRequestBodyNoFilesLimit` value. With the default settings, ModSecurity allows 12.5MB for `SecRequestBodyLimit` and 128kB for `SecRequestBodyNoFilesLimit` - see the [recommended](https://github.com/owasp-modsecurity/ModSecurity/blob/v2/master/modsecurity.conf-recommended#L45-L46) config file. This means: -* if the content type of the request is JSON, XML, or URL-encoded, then `SecRequestBodyNoFilesLimit` (the lower value) will be applied (even if the payload is extremely large, because this limit is much lower) -* if the content type is multipart, then the total size is checked against `SecRequestBodyLimit` **and** the non-file portion against `SecRequestBodyNoFilesLimit` +* if the content type of the request is JSON, XML, or URL-encoded, then `SecRequestBodyNoFilesLimit` (the lower value) will be the effective limit (even if the payload is extremely large, because this limit is much lower) +* if the content type is multipart, then the total size is checked against `SecRequestBodyLimit` **and** the non-file portion against `SecRequestBodyNoFilesLimit`, i.e., it mainly depends on the size of the uploaded file which limit will be reached first -**A very important note**: both configuration directives have a hard-coded limit in the v2 engine, which is 1GB (see the documentation above). In v3, there is no hard-coded limit, which is the expected behavior. We will remove this limit from v2 soon. +**A very important note**: both configuration directives have a hard-coded limit in the v2 engine, which is 1GB (see the documentation above). In v3, there is no hard-coded limit, which is the expected behavior (there are, of course, hard limits with respect to the hardware and memory word size). We will remove this hard-coded limit from v2 soon. -#### A mysterious SecRequestBodyLimitAction directive +#### The mysterious SecRequestBodyLimitAction directive -As I mentioned above, ModSecurity has a directive to handle this case: `SecRequestBodyLimitAction`. The possible values are `Reject` (the default) or `ProcessPartial`. This describes what the engine should do when the body exceeds the configured limit — with default values, what to do if the payload is greater than 12.5 MB. +As I mentioned above, ModSecurity has a directive to handle the case where the size of the request body exceeds one of the configured limits: `SecRequestBodyLimitAction`. The allowed values for the directive are `Reject` (the default) or `ProcessPartial`. `Reject` is clear: it terminates the connection with status 413.