Skip to content

[Security] jwt.decode() with audience= accepts tokens missing 'aud' claim (CWE-287) #407

@netpack

Description

@netpack

When jwt.decode() is called with an audience parameter, tokens that do not contain an aud claim are silently accepted instead of being rejected. This allows cross-service token reuse and potential privilege escalation.

Affected versions: All versions through 3.5.0 (latest)

Reproduction:

from jose import jwt

token = jwt.encode({"sub": "attacker", "role": "admin"}, "secret", algorithm="HS256")

This should raise but doesn't:

decoded = jwt.decode(token, "secret", algorithms=["HS256"], audience="my-service")

Returns: {'sub': 'attacker', 'role': 'admin'}

Expected behavior: When audience= is specified, tokens without an aud claim should be rejected (as PyJWT does with MissingRequiredClaimError).

Actual behavior: The token is accepted silently.

Impact: Any application using audience= without also setting options={"require_aud": True} is vulnerable to cross-service token reuse. An attacker with a valid token from one service (that doesn't set aud) can use it to access another service that expects a specific audience.

Comparison with PyJWT:

import jwt as pyjwt
token = pyjwt.encode({"sub": "attacker"}, "secret", algorithm="HS256")
pyjwt.decode(token, "secret", algorithms=["HS256"], audience="my-service")

Raises: MissingRequiredClaimError('aud')

Suggested fix:

In jose/jwt.py, _validate_aud():

def _validate_aud(claims, audience=None):
if "aud" not in claims:
if audience is not None:
raise JWTClaimsError("Token is missing the 'aud' claim")
return
# ... rest unchanged

Workaround: options={"require_aud": True} (non-obvious, not the default)

CWE: CWE-287 (Improper Authentication) / CWE-1188 (Insecure Default)

Discovered by: Frédéric Bogaerts, University of Coimbra (VAITP Research Project)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions