VNX-JWT-006 – JWT Missing Audience or Issuer Verification
Overview
This rule detects jwt.decode() calls (Python) and jwt.verify() calls (Node.js) that do not include audience, issuer, aud, or iss verification options. Without audience and issuer checks, a JWT that was legitimately issued for one service can be presented to a different service that shares the same signing key, and that second service will accept it as valid. This is a token cross-service replay attack. This maps to CWE-287 (Improper Authentication).
The JWT specification defines the aud (audience) claim to identify the recipients that the JWT is intended for, and the iss (issuer) claim to identify the principal that issued the JWT. Verifying both claims ensures that a token can only be used with the service it was issued for, by the issuer you trust. Libraries do not verify these claims by default — explicit options must be passed to each decode or verify call.
Severity: Medium | CWE: CWE-287 – Improper Authentication
Why This Matters
In a microservices architecture or multi-application environment, multiple services may share a signing key — for convenience or because tokens are issued by a central identity provider. Without audience verification, a JWT issued for the payments-service with a payload like { "sub": "user123", "role": "admin" } can be presented to the admin-console service. If both services share a key and neither verifies aud, the admin console accepts the token and grants admin access.
CAPEC-196 (Session Credential Falsification through Forging) and MITRE ATT&CK T1550.001 (Use Alternate Authentication Material: Application Access Token) document the token replay pattern. This attack requires no cryptographic capability — the attacker simply presents a valid token to an unintended service. It is particularly easy to exploit when the attacker has a legitimate account on one low-privilege service and uses their valid token against a higher-privilege service.
Issuer verification prevents a third-party identity provider — or a compromised internal service — from issuing tokens that a victim service accepts. Together, audience and issuer verification bind tokens to their intended context.
What Gets Flagged
# FLAGGED: jwt.decode without audience or issuer verification
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
# A token issued for "service-a" is accepted by "service-b"
// FLAGGED: jwt.verify without audience or issuer options
const payload = jwt.verify(token, secretKey);
// FLAGGED: jwt.verify with options object but no audience/issuer
const payload = jwt.verify(token, secretKey, { algorithms: ["HS256"] });
Remediation
Always pass
audienceandissuerparameters tojwt.decode()in Python.# SAFE: audience and issuer verified on every decode import jwt payload = jwt.decode( token, SECRET_KEY, algorithms=["HS256"], audience="payments-service", issuer="https://auth.example.com", )Pass
audienceandissuerin the options object tojwt.verify()in Node.js.// SAFE: audience and issuer verified on every verify call const payload = jwt.verify(token, secretKey, { algorithms: ["HS256"], audience: "payments-service", issuer: "https://auth.example.com", });Define expected audience and issuer values as application constants rather than inline strings, so they can be audited and are not silently empty in any deployment environment.
// SAFE: constants ensure the values are always explicitly defined const JWT_AUDIENCE = process.env.JWT_AUDIENCE; const JWT_ISSUER = process.env.JWT_ISSUER; if (!JWT_AUDIENCE || !JWT_ISSUER) { throw new Error("JWT_AUDIENCE and JWT_ISSUER must be configured"); } const payload = jwt.verify(token, secretKey, { algorithms: ["RS256"], audience: JWT_AUDIENCE, issuer: JWT_ISSUER, });
References
- CWE-287: Improper Authentication
- CAPEC-196: Session Credential Falsification through Forging
- MITRE ATT&CK T1550.001 – Use Alternate Authentication Material: Application Access Token
- RFC 7519 – JSON Web Token (JWT), Section 4.1: Registered Claim Names
- jwt.io – JWT introduction and claims
- OWASP JWT Security Cheat Sheet – Validate all claims
- PyJWT documentation – Audience and issuer verification