VYPR
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.

PackageAffected versionsPatched versions
body-parsernpm
< 1.20.31.20.3

Affected products

1

Patches

1
b2695c4450f0

Merge commit from fork

https://github.com/expressjs/body-parserUlises GascónSep 9, 2024via ghsa
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

News mentions

0

No linked articles in our index yet.