jose vulnerable to resource exhaustion via specifically crafted JWE with compressed plaintext
Description
jose is JavaScript module for JSON Object Signing and Encryption, providing support for JSON Web Tokens (JWT), JSON Web Signature (JWS), JSON Web Encryption (JWE), JSON Web Key (JWK), JSON Web Key Set (JWKS), and more. A vulnerability has been identified in the JSON Web Encryption (JWE) decryption interfaces, specifically related to the support for decompressing plaintext after its decryption. Under certain conditions it is possible to have the user's environment consume unreasonable amount of CPU time or memory during JWE Decryption operations. This issue has been patched in versions 2.0.7 and 4.15.5.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Jose JWE decryption vulnerable to resource exhaustion via compressed plaintext; patched in v2.0.7 and v4.15.5.
Vulnerability
Overview
CVE-2024-28176 is a resource exhaustion vulnerability in the JSON Web Encryption (JWE) decryption interface of the jose JavaScript module. The root cause lies in the module's support for decompressing plaintext after decryption. According to RFC 8725, compression before encryption is discouraged, but jose allowed it. An attacker can craft a JWE token with a highly compressed payload such that the token size is small but the decompressed plaintext is extremely large, leading to excessive CPU or memory consumption during decryption [1][4].
Exploitation
Conditions
Exploitation requires the attacker to send a specially crafted JWE token to an application that uses jose's JWE decryption APIs and accepts tokens from untrusted sources. The attack is limited to Node.js environments, where zlib decompression is implemented natively; runtimes like Deno or Cloudflare Workers are not affected because they lack built-in compression support [4]. Token size limits may be bypassed since the compressed token appears small, and after decryption, the decompression inflates the payload, overwhelming system resources.
Impact
Successful exploitation results in denial-of-service conditions through unreasonable CPU time or memory consumption. The attacker does not need authentication if the application processes JWEs from untrusted sources. The impact is confined to resource exhaustion; no data breach or integrity compromise is reported [1][4].
Mitigation
The issue is patched in jose versions 2.0.7 and 4.15.5. The fix introduces a maximum decompressed output limit of 250 kB, configurable via the inflateRawSyncLimit (v2.x) or inflateRaw decryption option (v4.x) [3][4]. Users of v5.x are unaffected as compression support was removed entirely. As a workaround, users unable to upgrade can reject tokens with a zip header indicating compression [4].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
josenpm | >= 3.0.0, < 4.15.5 | 4.15.5 |
jose-node-cjs-runtimenpm | < 4.15.5 | 4.15.5 |
jose-node-esm-runtimenpm | < 4.15.5 | 4.15.5 |
josenpm | < 2.0.7 | 2.0.7 |
Affected products
45- osv-coords44 versionspkg:apk/chainguard/sqlpadpkg:apk/chainguard/sqlpad-compatpkg:apk/wolfi/sqlpadpkg:apk/wolfi/sqlpad-compatpkg:npm/josepkg:npm/jose-node-cjs-runtimepkg:npm/jose-node-esm-runtimepkg:rpm/almalinux/aardvark-dnspkg:rpm/almalinux/buildahpkg:rpm/almalinux/buildah-testspkg:rpm/almalinux/cockpit-podmanpkg:rpm/almalinux/conmonpkg:rpm/almalinux/containernetworking-pluginspkg:rpm/almalinux/containers-commonpkg:rpm/almalinux/container-selinuxpkg:rpm/almalinux/critpkg:rpm/almalinux/criupkg:rpm/almalinux/criu-develpkg:rpm/almalinux/criu-libspkg:rpm/almalinux/crunpkg:rpm/almalinux/fuse-overlayfspkg:rpm/almalinux/josepkg:rpm/almalinux/libjosepkg:rpm/almalinux/libjose-develpkg:rpm/almalinux/libslirppkg:rpm/almalinux/libslirp-develpkg:rpm/almalinux/netavarkpkg:rpm/almalinux/oci-seccomp-bpf-hookpkg:rpm/almalinux/podmanpkg:rpm/almalinux/podman-catatonitpkg:rpm/almalinux/podman-dockerpkg:rpm/almalinux/podman-gvproxypkg:rpm/almalinux/podman-pluginspkg:rpm/almalinux/podman-remotepkg:rpm/almalinux/podman-testspkg:rpm/almalinux/python3-criupkg:rpm/almalinux/python3-podmanpkg:rpm/almalinux/runcpkg:rpm/almalinux/skopeopkg:rpm/almalinux/skopeo-testspkg:rpm/almalinux/slirp4netnspkg:rpm/almalinux/toolboxpkg:rpm/almalinux/toolbox-testspkg:rpm/almalinux/udica
< 7.4.1-r2+ 43 more
- (no CPE)range: < 7.4.1-r2
- (no CPE)range: < 7.4.1-r2
- (no CPE)range: < 7.4.1-r2
- (no CPE)range: < 7.4.1-r2
- (no CPE)range: >= 3.0.0, < 4.15.5
- (no CPE)range: < 4.15.5
- (no CPE)range: < 4.15.5
- (no CPE)range: < 2:1.10.0-1.module_el8.10.0+3792+03eaed9c
- (no CPE)range: < 2:1.33.7-2.el9_4
- (no CPE)range: < 2:1.33.7-2.el9_4
- (no CPE)range: < 84.1-1.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 3:2.1.10-1.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 1:1.4.0-2.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 2:1-81.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 2:2.229.0-2.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 3.18-5.module_el8.10.0+3845+87b84552
- (no CPE)range: < 3.18-5.module_el8.10.0+3845+87b84552
- (no CPE)range: < 3.18-5.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 3.18-5.module_el8.10.0+3845+87b84552
- (no CPE)range: < 1.14.3-2.module_el8.10.0+3845+87b84552
- (no CPE)range: < 1.13-1.module_el8.10.0+3792+03eaed9c
- (no CPE)range: < 10-2.el8_10.3
- (no CPE)range: < 10-2.el8_10.3
- (no CPE)range: < 10-2.el8_10.3
- (no CPE)range: < 4.4.0-2.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 4.4.0-2.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 2:1.10.3-1.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 1.2.10-1.module_el8.10.0+3792+03eaed9c
- (no CPE)range: < 4:4.9.4-4.el9_4
- (no CPE)range: < 4:4.9.4-3.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 4:4.9.4-4.el9_4
- (no CPE)range: < 4:4.9.4-3.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 4:4.9.4-4.el9_4
- (no CPE)range: < 4:4.9.4-4.el9_4
- (no CPE)range: < 4:4.9.4-4.el9_4
- (no CPE)range: < 3.18-5.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 4.9.0-1.module_el8.10.0+3792+03eaed9c
- (no CPE)range: < 1:1.1.12-1.module_el8.10.0+3845+87b84552
- (no CPE)range: < 2:1.14.3-2.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 2:1.14.3-2.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 1.2.3-1.module_el8.10.0+3845+87b84552
- (no CPE)range: < 0.0.99.5-2.module_el8.10.0+3845+87b84552
- (no CPE)range: < 0.0.99.5-2.module_el8.10.0+3858+6ad51f9f
- (no CPE)range: < 0.2.6-21.module_el8.10.0+3858+6ad51f9f
Patches
202a65794f787fix: add a maxOutputLength option to zlib inflate
4 files changed · +28 −2
docs/README.md+2 −0 modified@@ -1494,6 +1494,8 @@ operation. Count) Header Parameter value. The PBKDF2 iteration count defines the algorithm's computational expense. **Default:** '10000' + - `inflateRawSyncLimit`: `number` Limits compressed JWE plaintext output size. + **Default:** '250000' - `complete`: `<boolean>` When true returns an object with the parsed headers, verified AAD, the content encryption key, the key that was used to unwrap or derive the content encryption key, and cleartext instead of only the cleartext.
lib/jwe/decrypt.js+2 −2 modified@@ -52,7 +52,7 @@ const validateAlgorithms = (algorithms, option) => { /* * @public */ -const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], complete = false, keyManagementAlgorithms, contentEncryptionAlgorithms, maxPBES2Count = 10000 } = {}) => { +const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], complete = false, keyManagementAlgorithms, contentEncryptionAlgorithms, maxPBES2Count = 10000, inflateRawSyncLimit = 250000 } = {}) => { key = getKey(key, true) keyManagementAlgorithms = validateAlgorithms(keyManagementAlgorithms, 'keyManagementAlgorithms') @@ -189,7 +189,7 @@ const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], c let cleartext = decrypt(enc, cek, base64url.decodeToBuffer(ciphertext), { iv, tag, aad: adata }) if (opts.zip) { - cleartext = inflateRawSync(cleartext) + cleartext = inflateRawSync(cleartext, { maxOutputLength: inflateRawSyncLimit }) } if (complete) {
test/jwe/sanity.test.js+23 −0 modified@@ -1,4 +1,5 @@ const test = require('ava') +const crypto = require('crypto') const base64url = require('../../lib/help/base64url') const { JWKS, JWK: { generateSync }, JWE, errors } = require('../..') @@ -616,3 +617,25 @@ if (!('electron' in process.versions)) { }, { instanceOf: errors.JWEInvalid, message: 'JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds' }) }) } + +test('Compressed JWE output length limit', t => { + const k = generateSync('oct', 256) + { + const jwe = JWE.encrypt(crypto.randomBytes(250000), k, { alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + t.notThrows(() => { + JWE.decrypt(jwe, k) + }) + } + { + const jwe = JWE.encrypt(crypto.randomBytes(250000 + 1), k, { alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + t.throws(() => { + JWE.decrypt(jwe, k) + }) + } + { + const jwe = JWE.encrypt(crypto.randomBytes(1001), k, { alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + t.throws(() => { + JWE.decrypt(jwe, k, { inflateRawSyncLimit: 1000 }) + }) + } +})
types/index.d.ts+1 −0 modified@@ -402,6 +402,7 @@ export namespace JWE { contentEncryptionAlgorithms?: string[]; keyManagementAlgorithms?: string[]; maxPBES2Count?: number; + inflateRawSyncLimit?: number; } interface completeDecrypt {
1b91d88d2f82fix: add a maxOutputLength option to zlib inflate
4 files changed · +121 −1
docs/classes/util_errors.JWEDecompressionFailed.md+82 −0 added@@ -0,0 +1,82 @@ +# Class: JWEDecompressionFailed + +## [💗 Help the project](https://github.com/sponsors/panva) + +Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva). + +--- + +An error subclass thrown when a JWE ciphertext decompression fails. + +**`Example`** + +Checking thrown error is this one using a stable error code + +```js +if (err.code === 'ERR_JWE_DECOMPRESSION_FAILED') { + // ... +} +``` + +**`Example`** + +Checking thrown error is this one using `instanceof` + +```js +if (err instanceof jose.errors.JWEDecompressionFailed) { + // ... +} +``` + +## Table of contents + +### Constructors + +- [constructor](util_errors.JWEDecompressionFailed.md#constructor) + +### Properties + +- [code](util_errors.JWEDecompressionFailed.md#code) +- [message](util_errors.JWEDecompressionFailed.md#message) + +### Accessors + +- [code](util_errors.JWEDecompressionFailed.md#code-1) + +## Constructors + +### constructor + +• **new JWEDecompressionFailed**(`message?`) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `message?` | `string` | + +## Properties + +### code + +• **code**: `string` = `'ERR_JWE_DECOMPRESSION_FAILED'` + +A unique error code for the particular error subclass. + +___ + +### message + +• **message**: `string` = `'decompression operation failed'` + +## Accessors + +### code + +• `Static` `get` **code**(): ``"ERR_JWE_DECOMPRESSION_FAILED"`` + +A unique error code for the particular error subclass. + +#### Returns + +``"ERR_JWE_DECOMPRESSION_FAILED"``
docs/modules/util_errors.md+1 −0 modified@@ -13,6 +13,7 @@ Support from the community to continue maintaining and improving this module is - [JOSEAlgNotAllowed](../classes/util_errors.JOSEAlgNotAllowed.md) - [JOSEError](../classes/util_errors.JOSEError.md) - [JOSENotSupported](../classes/util_errors.JOSENotSupported.md) +- [JWEDecompressionFailed](../classes/util_errors.JWEDecompressionFailed.md) - [JWEDecryptionFailed](../classes/util_errors.JWEDecryptionFailed.md) - [JWEInvalid](../classes/util_errors.JWEInvalid.md) - [JWKInvalid](../classes/util_errors.JWKInvalid.md)
src/runtime/node/zlib.ts+2 −1 modified@@ -6,5 +6,6 @@ import type { InflateFunction, DeflateFunction } from '../../types.d' const inflateRaw = promisify(inflateRawCb) const deflateRaw = promisify(deflateRawCb) -export const inflate: InflateFunction = (input: Uint8Array) => inflateRaw(input) +export const inflate: InflateFunction = (input: Uint8Array) => + inflateRaw(input, { maxOutputLength: 250_000 }) export const deflate: DeflateFunction = (input: Uint8Array) => deflateRaw(input)
test/jwe/flattened.decrypt.test.mjs+36 −0 modified@@ -1,5 +1,7 @@ import test from 'ava' import * as crypto from 'crypto' +import { promisify } from 'node:util' +import { inflateRaw as inflateRawCb } from 'node:zlib' const { FlattenedEncrypt, flattenedDecrypt } = await import('#dist') @@ -228,3 +230,37 @@ test('decrypt PBES2 p2c limit', async (t) => { code: 'ERR_JWE_INVALID', }) }) + +test('decrypt inflate output length limit', async (t) => { + { + const jwe = await new FlattenedEncrypt(new Uint8Array(250000)) + .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + .encrypt(new Uint8Array(32)) + + await flattenedDecrypt(jwe, new Uint8Array(32)) + } + + { + const jwe = await new FlattenedEncrypt(new Uint8Array(250000 + 1)) + .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + .encrypt(new Uint8Array(32)) + + await t.throwsAsync(flattenedDecrypt(jwe, new Uint8Array(32)), { + message: 'decompression operation failed', + code: 'ERR_JWE_DECOMPRESSION_FAILED', + }) + } + + { + const jwe = await new FlattenedEncrypt(new Uint8Array(1000 + 1)) + .setProtectedHeader({ alg: 'dir', enc: 'A128CBC-HS256', zip: 'DEF' }) + .encrypt(new Uint8Array(32)) + + const inflateRawPromise = promisify(inflateRawCb) + const inflateRaw = async (buffer) => inflateRawPromise(buffer, { maxOutputLength: 1000 }) + + await t.throwsAsync(flattenedDecrypt(jwe, new Uint8Array(32), { inflateRaw }), { + code: 'ERR_BUFFER_TOO_LARGE', + }) + } +})
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
15- github.com/advisories/GHSA-hhhv-q57g-882qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-28176ghsaADVISORY
- github.com/panva/jose/commit/02a65794f7873cdaf12e81e80ad076fcdc4a9314ghsax_refsource_MISCWEB
- github.com/panva/jose/commit/1b91d88d2f8233f3477a5f4579aa5f8057b2ee8bghsax_refsource_MISCWEB
- github.com/panva/jose/security/advisories/GHSA-hhhv-q57g-882qghsax_refsource_CONFIRMWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/I6MMWFBOXJA6ZCXNVPDFJ4XMK5PVG5RGghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KXKGNCRU7OTM5AHC7YIYBNOWI742PRMYghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UG5FSEYJ3GP27FZXC5YAAMMEC5XWKJHGghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UJO2U5ACZVACNQXJ5EBRFLFW6DP5BROYghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/XJDO5VSIAOGT2WP63AXAAWNRSVJCNCRHghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/I6MMWFBOXJA6ZCXNVPDFJ4XMK5PVG5RG/mitre
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KXKGNCRU7OTM5AHC7YIYBNOWI742PRMY/mitre
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UG5FSEYJ3GP27FZXC5YAAMMEC5XWKJHG/mitre
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UJO2U5ACZVACNQXJ5EBRFLFW6DP5BROY/mitre
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/XJDO5VSIAOGT2WP63AXAAWNRSVJCNCRH/mitre
News mentions
0No linked articles in our index yet.