VYPR
High severityNVD Advisory· Published Mar 3, 2026· Updated Mar 4, 2026

joserfc PBES2 p2c Unbounded Iteration Count enables Denial of Service (DoS)

CVE-2026-27932

Description

joserfc is a Python library that provides an implementation of several JSON Object Signing and Encryption (JOSE) standards. In 1.6.2 and earlier, a resource exhaustion vulnerability in joserfc allows an unauthenticated attacker to cause a Denial of Service (DoS) via CPU exhaustion. When the library decrypts a JSON Web Encryption (JWE) token using Password-Based Encryption (PBES2) algorithms, it reads the p2c (PBES2 Count) parameter directly from the token's protected header. This parameter defines the number of iterations for the PBKDF2 key derivation function. Because joserfc does not validate or bound this value, an attacker can specify an extremely large iteration count (e.g., 2^31 - 1), forcing the server to expend massive CPU resources processing a single token. This vulnerability exists at the JWA layer and impacts all high-level JWE and JWT decryption interfaces if PBES2 algorithms are allowed by the application's policy.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2026-27932: In joserfc <=1.6.2, unauthenticated attackers cause CPU exhaustion by providing an unbounded PBES2 iteration count in JWE tokens.

Vulnerability

Description CVE-2026-27932 is a resource exhaustion vulnerability in the Python joserfc library (versions 1.6.2 and earlier). The issue lies in how the library processes Password-Based Encryption (PBES2) algorithms during JWE decryption. Specifically, the p2c (PBES2 Count) parameter in a JWE token's protected header, which controls the number of PBKDF2 iterations, is taken directly from the attacker-controlled token without any validation or upper bound [1][2]. The vulnerable code path is in the PBES2HSAlgKeyEncryption.decrypt_cek() function, which passes the p2c value straight into the PBKDF2HMAC constructor [2].

Attack

Vector and Exploitation An unauthenticated attacker can exploit this by crafting a JWE token with an arbitrarily large p2c value (e.g., 2^31 - 1) and sending it to any server that uses joserfc with PBES2 algorithms enabled. The server will then compute an extremely expensive PBKDF2 key derivation, consuming massive CPU resources before any token validation (such as expiration or signature checks) occurs [2]. This makes the attack trivially accessible to any network-entity that can deliver HTTP requests to the vulnerable endpoint.

Impact

The primary impact is a Denial of Service (DoS) via CPU exhaustion. A single malicious token can tie up server resources for an extended period, potentially degrading or halting service for legitimate users [1][2]. Because the vulnerability operates at the JWA layer, it affects all high-level JWE and JWT decryption interfaces, meaning any application policy that permits PBES2 algorithms is immediately at risk [2].

Mitigation

The joserfc maintainers have released a fix in commit 696a961, which introduces a validate_p2c function that enforces a maximum iteration count of 300,000 (and warns if below 1,000) [3][4]. Users should update to a patched version or apply the commit's changes manually. As a workaround, applications can disable PBES2 key management algorithms in their JWT policies until the update is applied [1][2].

AI Insight generated on May 18, 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.

PackageAffected versionsPatched versions
joserfcPyPI
< 1.6.31.6.3

Affected products

2
  • joserfc/joserfcllm-create
    Range: <=1.6.2
  • authlib/joserfcv5
    Range: <= 1.6.2

Patches

2
696a9611ab98

fix(jwe): set max value for p2c

https://github.com/authlib/joserfcHsiaoming YangFeb 25, 2026via ghsa
2 files changed · +62 2
  • src/joserfc/_rfc7518/jwe_algs.py+17 2 modified
    @@ -1,5 +1,7 @@
     from __future__ import annotations
     import secrets
    +import warnings
    +
     from cryptography.hazmat.primitives.asymmetric import padding
     from cryptography.hazmat.primitives import hashes
     from cryptography.hazmat.primitives.keywrap import (
    @@ -30,6 +32,7 @@
     from ..errors import (
         InvalidKeyLengthError,
         DecodeError,
    +    SecurityWarning,
     )
     
     
    @@ -224,16 +227,28 @@ def decrypt_agreed_upon_key(self, enc: JWEEncModel, recipient: Recipient[ECKey])
             return derive_key_for_concat_kdf(shared_key, headers, enc.cek_size, self.key_size)
     
     
    +def validate_p2c(value: int) -> None:
    +    if not isinstance(value, int):
    +        raise ValueError("must be an int")
    +
    +    # A minimum iteration count of 1000 is RECOMMENDED.
    +    if value < 1000:
    +        warnings.warn("A minimum iteration count of 1000 is RECOMMENDED", SecurityWarning)
    +
    +    max_value = 300000
    +    if value > max_value:
    +        raise ValueError(f"must be less than {max_value}")
    +
    +
     class PBES2HSAlgKeyEncryption(JWEKeyEncryption):
         # https://www.rfc-editor.org/rfc/rfc7518#section-4.8
         key_size: int
         more_header_registry = {
             "p2s": HeaderParameter("PBES2 Salt Input", "str", True),
    -        "p2c": HeaderParameter("PBES2 Count", "int", True),
    +        "p2c": HeaderParameter("PBES2 Count", validate_p2c, True),
         }
         key_types = ["oct"]
     
    -    # A minimum iteration count of 1000 is RECOMMENDED.
         DEFAULT_P2C = 2048
     
         def __init__(self, hash_size: int, key_wrapping: JWEKeyWrapping):
    
  • tests/jwe/test_compact.py+45 0 modified
    @@ -8,6 +8,7 @@
     from joserfc.jwa import JWE_ENC_MODELS
     from joserfc.jwk import RSAKey, ECKey, OctKey, OKPKey, KeySet
     from joserfc.errors import (
    +    SecurityWarning,
         InvalidKeyLengthError,
         MissingAlgorithmError,
         MissingEncryptionError,
    @@ -180,6 +181,50 @@ def test_PBES2HS_with_header(self):
                 registry=registry,
             )
     
    +    def test_PBES2HS_with_small_p2c(self):
    +        key = OctKey.generate_key(128)
    +        protected = {
    +            "alg": "PBES2-HS256+A128KW",
    +            "enc": "A128CBC-HS256",
    +            "p2s": "QoGrcBpns_cLWCQPEVuA-g",
    +            "p2c": 500,
    +        }
    +        registry = JWERegistry(algorithms=["PBES2-HS256+A128KW", "A128CBC-HS256"])
    +        self.assertWarns(
    +            SecurityWarning,
    +            encrypt_compact,
    +            protected,
    +            b"i",
    +            key,
    +            registry=registry,
    +        )
    +
    +    def test_PBES2HS_with_large_p2c(self):
    +        key = OctKey.import_key({"k": "pyL42ncDFSYnenl-GiZjRw", "kty": "oct"})
    +        protected = {
    +            "alg": "PBES2-HS256+A128KW",
    +            "enc": "A128CBC-HS256",
    +            "p2s": "QoGrcBpns_cLWCQPEVuA-g",
    +            "p2c": 500000,
    +        }
    +        registry = JWERegistry(algorithms=["PBES2-HS256+A128KW", "A128CBC-HS256"])
    +        self.assertRaises(
    +            InvalidHeaderValueError,
    +            encrypt_compact,
    +            protected,
    +            b"i",
    +            key,
    +            registry=registry,
    +        )
    +        encrypted = "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwicDJzIjoiUW9HcmNCcG5zX2NMV0NRUEVWdUEtZyIsInAyYyI6NTAwMDAwfQ.qdtshVQlPM-fW57DRVUnmwMyvBVzUZCm58zn7j5W7IP9S2-cBVTh_w.mMUagTUTRi7fLQ3VUi6g4w.Hi0-8_MusxEwRtW6dkjXzw.Ktm1FmBA9rPe0Vv8w0kZ2g"
    +        self.assertRaises(
    +            InvalidHeaderValueError,
    +            decrypt_compact,
    +            encrypted,
    +            key,
    +            registry=registry,
    +        )
    +
         def test_with_zip_header(self):
             private_key: RSAKey = load_key("rsa-openssl-private.pem")
             public_key: RSAKey = load_key("rsa-openssl-public.pem")
    
696a961

fix(jwe): set max value for p2c

https://github.com/authlib/joserfcHsiaoming YangFeb 25, 2026via ghsa
2 files changed · +62 2
  • src/joserfc/_rfc7518/jwe_algs.py+17 2 modified
    @@ -1,5 +1,7 @@
     from __future__ import annotations
     import secrets
    +import warnings
    +
     from cryptography.hazmat.primitives.asymmetric import padding
     from cryptography.hazmat.primitives import hashes
     from cryptography.hazmat.primitives.keywrap import (
    @@ -30,6 +32,7 @@
     from ..errors import (
         InvalidKeyLengthError,
         DecodeError,
    +    SecurityWarning,
     )
     
     
    @@ -224,16 +227,28 @@ def decrypt_agreed_upon_key(self, enc: JWEEncModel, recipient: Recipient[ECKey])
             return derive_key_for_concat_kdf(shared_key, headers, enc.cek_size, self.key_size)
     
     
    +def validate_p2c(value: int) -> None:
    +    if not isinstance(value, int):
    +        raise ValueError("must be an int")
    +
    +    # A minimum iteration count of 1000 is RECOMMENDED.
    +    if value < 1000:
    +        warnings.warn("A minimum iteration count of 1000 is RECOMMENDED", SecurityWarning)
    +
    +    max_value = 300000
    +    if value > max_value:
    +        raise ValueError(f"must be less than {max_value}")
    +
    +
     class PBES2HSAlgKeyEncryption(JWEKeyEncryption):
         # https://www.rfc-editor.org/rfc/rfc7518#section-4.8
         key_size: int
         more_header_registry = {
             "p2s": HeaderParameter("PBES2 Salt Input", "str", True),
    -        "p2c": HeaderParameter("PBES2 Count", "int", True),
    +        "p2c": HeaderParameter("PBES2 Count", validate_p2c, True),
         }
         key_types = ["oct"]
     
    -    # A minimum iteration count of 1000 is RECOMMENDED.
         DEFAULT_P2C = 2048
     
         def __init__(self, hash_size: int, key_wrapping: JWEKeyWrapping):
    
  • tests/jwe/test_compact.py+45 0 modified
    @@ -8,6 +8,7 @@
     from joserfc.jwa import JWE_ENC_MODELS
     from joserfc.jwk import RSAKey, ECKey, OctKey, OKPKey, KeySet
     from joserfc.errors import (
    +    SecurityWarning,
         InvalidKeyLengthError,
         MissingAlgorithmError,
         MissingEncryptionError,
    @@ -180,6 +181,50 @@ def test_PBES2HS_with_header(self):
                 registry=registry,
             )
     
    +    def test_PBES2HS_with_small_p2c(self):
    +        key = OctKey.generate_key(128)
    +        protected = {
    +            "alg": "PBES2-HS256+A128KW",
    +            "enc": "A128CBC-HS256",
    +            "p2s": "QoGrcBpns_cLWCQPEVuA-g",
    +            "p2c": 500,
    +        }
    +        registry = JWERegistry(algorithms=["PBES2-HS256+A128KW", "A128CBC-HS256"])
    +        self.assertWarns(
    +            SecurityWarning,
    +            encrypt_compact,
    +            protected,
    +            b"i",
    +            key,
    +            registry=registry,
    +        )
    +
    +    def test_PBES2HS_with_large_p2c(self):
    +        key = OctKey.import_key({"k": "pyL42ncDFSYnenl-GiZjRw", "kty": "oct"})
    +        protected = {
    +            "alg": "PBES2-HS256+A128KW",
    +            "enc": "A128CBC-HS256",
    +            "p2s": "QoGrcBpns_cLWCQPEVuA-g",
    +            "p2c": 500000,
    +        }
    +        registry = JWERegistry(algorithms=["PBES2-HS256+A128KW", "A128CBC-HS256"])
    +        self.assertRaises(
    +            InvalidHeaderValueError,
    +            encrypt_compact,
    +            protected,
    +            b"i",
    +            key,
    +            registry=registry,
    +        )
    +        encrypted = "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwicDJzIjoiUW9HcmNCcG5zX2NMV0NRUEVWdUEtZyIsInAyYyI6NTAwMDAwfQ.qdtshVQlPM-fW57DRVUnmwMyvBVzUZCm58zn7j5W7IP9S2-cBVTh_w.mMUagTUTRi7fLQ3VUi6g4w.Hi0-8_MusxEwRtW6dkjXzw.Ktm1FmBA9rPe0Vv8w0kZ2g"
    +        self.assertRaises(
    +            InvalidHeaderValueError,
    +            decrypt_compact,
    +            encrypted,
    +            key,
    +            registry=registry,
    +        )
    +
         def test_with_zip_header(self):
             private_key: RSAKey = load_key("rsa-openssl-private.pem")
             public_key: RSAKey = load_key("rsa-openssl-public.pem")
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.