Unverified JWT Decode

HIGH

Detects jwt.decode() calls that may bypass signature verification, allowing tampered tokens to be accepted.

Rule Information

Language
Python
Category
JWT
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythonjwtpyjwtunverified-decodesignature-bypassauthenticationtoken-verificationCWE-287CWE-345OWASP-A07
CWE References

Interactive Playground

Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.

pathfinder scan --ruleset python/PYTHON-JWT-SEC-003 --project .
1
2
3
4
5
6
7
8
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

About This Rule

Understanding the vulnerability and how it is detected

This rule detects jwt.decode() calls where signature verification might be disabled. The dangerous pattern is passing options={"verify_signature": False} to jwt.decode(), which tells PyJWT to skip signature checking entirely. The result is the same as using algorithm="none" -- any token, forged or modified, will be accepted.

This pattern appears more often than you'd expect. Developers disable verification during debugging ("I just want to read the claims"), forget to re-enable it, and ship it to production. Or they disable it because they're "verifying the token elsewhere" -- but that elsewhere doesn't exist or has its own bugs.

The rule currently operates at audit level, flagging all jwt.decode() calls for review. This is because the insecure configuration is a nested dict value (options={"verify_signature": False}) that the engine can't precisely match yet. Future engine updates will add nested keyword matching to flag only the unsafe configuration.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Token Tampering

Without signature verification, an attacker can modify any field in the JWT payload -- user ID, role, permissions, expiration -- and the application will accept the modified token as valid. The signature exists specifically to prevent this.

2

Identity Spoofing

An attacker can decode their own low-privilege token, change the subject claim to another user's ID, and submit it. Without signature verification, the application can't tell the difference between a legitimate token and a forged one.

3

Session Hijacking

If tokens aren't verified, an attacker doesn't even need to steal a valid token. They can create one from scratch with any claims they want. This bypasses all session management controls.

4

Expiration Bypass

Disabling verify_signature often disables expiration checking too, depending on the options dict. An attacker with an expired token can keep using it indefinitely.

How to Fix

Recommended remediation steps

  • 1Never set verify_signature to False in production -- if you need to read claims without verification, use jwt.decode(token, options={"verify_signature": False}) only in debug/test code and gate it behind an environment check
  • 2Always pass an explicit algorithms list to jwt.decode() to prevent algorithm confusion attacks
  • 3Use options={"require": ["exp", "iat"]} to ensure tokens have required claims
  • 4Consider wrapping jwt.decode() in a helper function that enforces verification, so individual call sites can't accidentally disable it
  • 5In PyJWT 1.x, the parameter was verify=False -- if you're migrating from 1.x to 2.x, search for both patterns

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule matches all jwt.decode() calls resolved through the PyJWT library using QueryType-based type inference. It currently operates as an audit rule that flags all jwt.decode() calls for review. The rule cannot yet distinguish between safe decodes (with verification) and unsafe decodes (with verify_signature=False) because this requires nested keyword argument matching into the options dict.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-287 ranked #13 in 2023 Most Dangerous Software Weaknesses
OWASP Top 10
A07:2021 - Identification and Authentication Failures
PCI DSS v4.0
Requirement 8.3 -- strong authentication for access to system components
NIST SP 800-63B
Section 7.1 -- verify signed assertions before accepting claims
SOC 2
CC6.1 -- logical access controls and authentication mechanisms

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about Unverified JWT Decode

Almost never in production. The only legitimate use is reading claims from a token you've already verified elsewhere -- for example, extracting the "sub" claim for logging after the API gateway already verified the signature. Even then, it's safer to pass the verified payload object rather than re-decoding the raw token.
The insecure configuration is a nested dict value -- options={"verify_signature": False}. The engine can match simple keyword arguments like algorithm="none", but can't yet inspect values inside dict arguments. This is tracked as a product roadmap item. For now, the rule flags all jwt.decode() calls so you can review each one.
In PyJWT 1.x, you disabled verification with jwt.decode(token, verify=False). In PyJWT 2.x, it moved to jwt.decode(token, options={"verify_signature": False}). If you're migrating a codebase, search for both patterns. Both are equally dangerous.
Yes. In PyJWT 2.x, signature verification is enabled by default. You have to explicitly disable it by passing options={"verify_signature": False}. This means the vulnerability requires an intentional (though misguided) code change.
Run: pathfinder ci --ruleset python/jwt --project . It outputs SARIF, JSON, or CSV. On GitHub, it posts inline review comments directly on pull requests pointing to the exact lines. No dashboard needed.
SEC-002 catches unsigned tokens at creation time (jwt.encode with algorithm="none"). This rule catches disabled verification at decode time (jwt.decode with verify_signature=False). Both result in the same outcome -- unverified tokens -- but at different points in the token lifecycle. You need both rules for complete coverage.

New feature

Get these findings posted directly on your GitHub pull requests

The Unverified JWT Decode rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works