CVE-2026-102269: PyJWT: Non-canonical signature segments enable raw-token revocation bypass

Published Sep 28, 2026
·
Updated

Summary

PyJWT 2.13.0 accepts compact JWS signature segments containing characters that are not in the Base64URL alphabet. Appending !!!! to a valid signature does not change the decoded signature bytes or authenticated claims, but it changes the serialized token and its SHA-256 hash. An application that indexes logout or revocation state by the raw token therefore rejects the logged-out token and accepts its no-key alternate serialization.

This report separates two issues: permissive compact-JWS decoding is the library behavior, while using raw serialized JWT text as a security identity is a dangerous application composition. A jti-indexed control blocks both representations.

Affected Version

Confirmed with PyJWT 2.13.0 on CPython 3.13. The mutation changes only the signature segment and requires no signing key.

End-to-End Reproduction

bash .venv-research/bin/python 06-practical-testing/revocation-bypass-lab.py

The loopback-only lab performs:

1. Issue a valid HS256 admin token. 2. Access /protected successfully. 3. Log out the canonical token and store SHA256(rawtoken). 4. Confirm canonical replay returns HTTP 401. 5. Append !!!! to only the signature segment. 6. Confirm the mutated token has the same verified claims and signature bytes, a different raw hash, and receives HTTP 200. 7. Repeat with jti-indexed revocation and confirm both forms return HTTP 401.

Observed statuses for raw-token revocation were 200 -> logout -> 401 for the canonical token and 200 for the alternate serialization. The jti control returned 401 for both replays after logout.

Security Boundaries and Severity

This is not signature forgery: the attacker starts with a valid token and cannot alter its claims. Impact requires a consumer to bind revocation, replay state, rate limits, or cache identity to the raw compact string. Under that composition, an authenticated user can bypass logout or token-specific revocation without the signing key. Medium is suggested for triage because the security impact is real but application-dependent.

Recommended Fix

- Reject signature segments containing characters outside [A-Za-z0-9-]. - Reject padding and other non-canonical Base64URL encodings for compact JWS. - Add regression tests proving malformed signature encodings fail before or during verification. - Document that applications must use a semantic identifier such as jti, not the raw serialized token, for revocation and replay state.

Maintainer update — 2026-09-09

We reproduced the reported behavior on the verified master commit 8915570: appending !!!! to a valid compact-JWS signature segment is accepted, produces the same decoded signature bytes and claims, and changes the serialized token hash. Existing JWS verification controls pass. The behavior is present in the tested release range represented by tags 2.4.0, 2.8.0, 2.10.1, 2.12.1, and 2.13.0.

The security impact is conditional on an application using the raw serialized token as the identity for logout, revocation, replay, rate-limit, or cache state. Under that composition, an attacker who already holds a valid token can use an alternate serialization after the canonical string is revoked. This is not signature forgery or claim alteration; applications should use a semantic identifier such as jti for token identity.

The report is ready to be accepted as a draft for continued remediation. A narrow fix assessment recommends rejecting non-canonical Base64URL characters and padding in compact-JWS segments. No fix commit or release is claimed yet.

Maintainer update — 2026-09-09

The fix has been rebased onto the current master as e6f48401001609a8f99e71fcaf355fb895d508a8 and is ready to be pushed. Compact-JWS header, payload, and signature segments are now required to use the unpadded Base64URL alphabet and canonical re-encoding; a signature segment containing non-canonical characters such as !!!! is rejected before verification. Valid detached b64=false handling remains supported.

Verification on the rebased fix commit passed 372 tests with 4 intentional cryptography-environment skips; Ruff formatting/lint and git diff --check passed. The compressed-payload regression fixture was updated to valid canonical compact-JWS encoding. No release containing the fix has been published yet, so the patched version remains unset pending release planning.

The advisory remains medium severity and unpublished.

Classification update — 2026-09-10

We completed the advisory classification review. The proposed CVSS 3.1 vector is CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N and the proposed CWE classification is CWE-180. The issue permits alternate non-canonical serializations of an already valid signed token to bypass applications that identify tokens by their raw serialization; it does not forge signatures or alter claims. CWE-180 captures validation/canonicalization ordering.

These metadata changes do not alter the affected range, fix status, or lifecycle state.

Maintainer update — 2026-09-11

The verified fix for this advisory is included in PyJWT 2.14.0, released on 2026-09-11 and available on PyPI. PyJWT 2.14.0 is the first release containing the fix. This advisory is now published with 2.14.0 recorded as the patched version.

Other sources

PyJWT is a Python implementation of JSON Web Token standards. Prior to 2.14.0, PyJWT signature segment is affected because signature segment decoding accepts characters outside the canonical Base64URL representation. This occurs when non-Base64URL characters are appended to a valid compact JWS signature segment. As a result, base64urldecode produces the same signature bytes for different serialized segments. Consequently, raw-token revocation checks can fail to recognize an equivalent modified token. This issue is fixed in version 2.14.0.

— NVD

PyJWT: Non-canonical signature segments enable raw-token revocation bypass

— Microsoft

Affected Software

4 affected componentsFixes available
pypi/pyjwt<2.14.0
pip/PyJWT<=2.13.0
2.14.0
Microsoft azl3 python-jwt 2.13.0-1<2.15.1-1
2.15.1-1
Pyjwt Project Pyjwt<2.14.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/PyJWT to a version that resolves this vulnerability.

    Fixed in 2.14.0
  2. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 2.15.1-1
  3. Upgrade

    Upgrade PyJWT to a version that resolves this vulnerability.

    Fixed in 2.14.0
  4. Configuration

    Use the semantic JWT identifier jti rather than the raw serialized token as the identity for revocation and replay state.

    Application JWT revocation, logout, replay, rate-limit, and cache state token identity = jti

Event History

Sep 28, 2026
CVE Published
via MITRE·08:27 PM
Data Sourced
via MITRE·08:27 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·09:17 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·09:17 PM
RemedyAffected Software
Sep 29, 2026
Advisory Published
via GitHub·11:17 PM
Data Sourced
via GitHub·11:17 PM
DescriptionSeverityWeaknessAffected Software
Sep 30, 2026
Data Sourced
via Microsoft·08:03 AM
DescriptionSeverityWeaknessAffected Software
Updated
via Microsoft·08:03 AM
DescriptionSeverity

Frequently Asked Questions

1

Which deployments are exposed to this issue?

Deployments using PyJWT versions earlier than 2.14.0 are affected if they perform token revocation by comparing the raw serialized token string. The bypass relies on treating differently serialized signature segments as distinct tokens even though they decode to the same signature bytes.

2

What does an attacker need to exploit the revocation bypass?

An attacker needs a valid compact JWS token whose raw serialized value has been revoked, and must be able to present a modified version with non-Base64URL characters appended to its signature segment. The modified segment decodes to the same signature bytes, allowing signature validation to succeed while a raw-token revocation lookup may not match it.

3

What should be done if patching cannot happen immediately?

Avoid relying solely on raw serialized token strings for revocation checks. Revocation logic should account for equivalent token encodings rather than assuming each serialized token string uniquely represents its decoded signature.

4

How can teams determine whether their application is affected?

Check whether PyJWT is below version 2.14.0 and whether revoked tokens are identified by their complete raw compact-JWS string. Test whether appending non-Base64URL characters to a valid token's signature segment preserves successful validation while causing the revocation lookup to miss the altered string.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203