High severityNVD Advisory· Published Sep 10, 2024· Updated Sep 10, 2024
body-parser vulnerable to denial of service when url encoding is enabled
CVE-2024-45590
Description
body-parser is Node.js body parsing middleware. body-parser <1.20.3 is vulnerable to denial of service when url encoding is enabled. A malicious actor using a specially crafted payload could flood the server with a large number of requests, resulting in denial of service. This issue is patched in 1.20.3.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
body-parsernpm | < 1.20.3 | 1.20.3 |
Affected products
1- Range: < 1.20.3
Patches
1b2695c4450f0Merge commit from fork
5 files changed · +124 −12
HISTORY.md+5 −2 modified@@ -1,6 +1,9 @@ unreleased -========== - * deps: qs@6.12.3 +=================== + + * deps: qs@6.13.0 + * add `depth` option to customize the depth level in the parser + * IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`) 1.20.2 / 2023-02-21 ===================
lib/types/urlencoded.js+30 −7 modified@@ -55,6 +55,9 @@ function urlencoded (options) { : opts.limit var type = opts.type || 'application/x-www-form-urlencoded' var verify = opts.verify || false + var depth = typeof opts.depth !== 'number' + ? Number(opts.depth || 32) + : opts.depth if (verify !== false && typeof verify !== 'function') { throw new TypeError('option verify must be function') @@ -118,7 +121,8 @@ function urlencoded (options) { encoding: charset, inflate: inflate, limit: limit, - verify: verify + verify: verify, + depth: depth }) } } @@ -133,12 +137,20 @@ function extendedparser (options) { var parameterLimit = options.parameterLimit !== undefined ? options.parameterLimit : 1000 + + var depth = typeof options.depth !== 'number' + ? Number(options.depth || 32) + : options.depth var parse = parser('qs') if (isNaN(parameterLimit) || parameterLimit < 1) { throw new TypeError('option parameterLimit must be a positive number') } + if(isNaN(depth) || depth < 0) { + throw new TypeError('option depth must be a zero or a positive number') + } + if (isFinite(parameterLimit)) { parameterLimit = parameterLimit | 0 } @@ -156,12 +168,23 @@ function extendedparser (options) { var arrayLimit = Math.max(100, paramCount) debug('parse extended urlencoding') - return parse(body, { - allowPrototypes: true, - arrayLimit: arrayLimit, - depth: Infinity, - parameterLimit: parameterLimit - }) + try { + return parse(body, { + allowPrototypes: true, + arrayLimit: arrayLimit, + depth: depth, + strictDepth: true, + parameterLimit: parameterLimit + }) + } catch (err) { + if (err instanceof RangeError) { + throw createError(400, 'The input exceeded the depth', { + type: 'querystring.parse.rangeError' + }) + } else { + throw err + } + } } }
package.json+1 −1 modified@@ -17,7 +17,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.12.3", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0"
README.md+8 −0 modified@@ -278,6 +278,10 @@ The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)` where `buf` is a `Buffer` of the raw request body and `encoding` is the encoding of the request. The parsing can be aborted by throwing an error. +#### depth + +The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible. + ## Errors The middlewares provided by this module create errors using the @@ -374,6 +378,10 @@ as well as in the `encoding` property. The `status` property is set to `415`, the `type` property is set to `'encoding.unsupported'`, and the `encoding` property is set to the encoding that is unsupported. +### The input exceeded the depth + +This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown. + ## Examples ### Express/Connect top-level generic
test/urlencoded.js+80 −2 modified@@ -195,7 +195,7 @@ describe('bodyParser.urlencoded()', function () { it('should parse deep object', function (done) { var str = 'foo' - for (var i = 0; i < 500; i++) { + for (var i = 0; i < 32; i++) { str += '[p]' } @@ -213,13 +213,91 @@ describe('bodyParser.urlencoded()', function () { var depth = 0 var ref = obj.foo while ((ref = ref.p)) { depth++ } - assert.strictEqual(depth, 500) + assert.strictEqual(depth, 32) }) .expect(200, done) }) }) }) + + describe('with depth option', function () { + describe('when custom value set', function () { + + it('should reject non possitive numbers', function () { + assert.throws(createServer.bind(null, { extended: true, depth: -1 }), + /TypeError: option depth must be a zero or a positive number/) + assert.throws(createServer.bind(null, { extended: true, depth: NaN }), + /TypeError: option depth must be a zero or a positive number/) + assert.throws(createServer.bind(null, { extended: true, depth: "beep" }), + /TypeError: option depth must be a zero or a positive number/) + }) + + + it('should parse up to the specified depth', function (done) { + this.server = createServer({ extended:true, depth: 10 }) + request(this.server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('a[b][c][d]=value') + .expect(200, '{"a":{"b":{"c":{"d":"value"}}}}', done) + }) + + it('should not parse beyond the specified depth', function (done) { + this.server = createServer({ extended:true, depth: 1 }) + request(this.server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send('a[b][c][d][e]=value') + .expect(400, '[querystring.parse.rangeError] The input exceeded the depth', done) + }) + }) + + + describe('when default value', function () { + before(function () { + this.server = createServer({ }) + }) + + it('should parse deeply nested objects', function (done) { + var deepObject = 'a' + for (var i = 0; i < 32; i++) { + deepObject += '[p]' + } + deepObject += '=value' + + request(this.server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(deepObject) + .expect(function (res) { + var obj = JSON.parse(res.text) + var depth = 0 + var ref = obj.a + while ((ref = ref.p)) { depth++ } + assert.strictEqual(depth, 32) + }) + .expect(200, done) + }) + + it('should not parse beyond the specified depth', function (done) { + var deepObject = 'a'; + for (var i = 0; i < 33; i++) { + deepObject += '[p]'; + } + deepObject += '=value'; + + request(this.server) + .post('/') + .set('Content-Type', 'application/x-www-form-urlencoded') + .send(deepObject) + .expect(400, '[querystring.parse.rangeError] The input exceeded the depth', done); + }); + + }) + + }) + describe('with inflate option', function () { describe('when false', function () { before(function () {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-qwcr-r2fm-qrc7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-45590ghsaADVISORY
- github.com/expressjs/body-parser/commit/b2695c4450f06ba3b0ccf48d872a229bb41c9bceghsax_refsource_MISCWEB
- github.com/expressjs/body-parser/security/advisories/GHSA-qwcr-r2fm-qrc7ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.