JSON Web Tokens (JWTs)
A JSON Web Token (JWT) is a compact, URL-safe token format for passing signed claims between parties, defined by RFC 7519. Format:
$HEADER.$PAYLOAD.$SIGNATURE
Each segment is base64url-encoded. JWTs are commonly passed as a cookie, an HTTP Authorization: Bearer header, or pulled from browser local storage.
$HEADERis a JSON blob like{ "alg": "RS256", "typ": "JWT" }, wherealgnames the signing algorithm the server expects.$PAYLOADis a JSON blob of claims — user identity, roles, expiry (exp), issuer (iss), etc. See jwt.io for a detailed breakdown of registered claims.$SIGNATUREis binary data: the signature of the string$HEADER.$PAYLOAD(both already base64url-encoded) usingalgand a server-side secret — often an RSA/EC private key, but sometimes just a symmetric string.
Use basenc --base64url / basenc -d --base64url (rather than plain base64) to encode/decode URL-safe base64 on the command line, and strip the trailing = padding.
Attack: the “none” algorithm
The JWT spec includes a none algorithm intended for cases where integrity is provided by other means. If a server accepts alg: "none", the signature segment is empty and the token is just $HEADER.$PAYLOAD. (note the trailing dot). An attacker can then edit the $PAYLOAD arbitrarily — escalate privileges, impersonate other users — and the server will accept the forged token.
The base64url encoding of {"typ":"JWT","alg":"none"} is eyJ0eX...lIn0.
Mitigation: explicitly allowlist acceptable algorithms server-side and never accept none in production.
Attack: RS256 → HS256 confusion (public key as HMAC secret)
If a server uses RS256 (asymmetric) but doesn’t strictly pin the algorithm, an attacker can flip alg to HS256 (symmetric HMAC). The server then verifies the signature using the public key as the HMAC secret — and public keys are, by definition, public. If the public half of the keypair is available (for example, re-used as the server’s TLS certificate), harvest it and forge valid JWTs at will.
The base64url encoding of {"typ":"JWT","alg":"HS256"} is eyJ0eX...J9Cg.
Sign $HEADER.$PAYLOAD with the PEM public key using:
echo -n "$HEADER.$PAYLOAD" | \
openssl dgst -sha256 -mac HMAC -macopt hexkey:$(cat $PUBLIC_KEY_FILE | xxd -p | tr -d '\n') | \
sed -e 's/.*= //' | \
tr -d '\n' | \
xxd -p -r | \
basenc --base64url | \
sed -e 's/=*$//'Mitigation: pin a single algorithm per key, and never use the same key for both asymmetric signing and symmetric verification.
Attack: brute-forcing weak HMAC secrets
When alg is HS256 and the signing secret is a simple string (not a high-entropy key), it can be brute-forced offline: take a captured token, compute HMAC-SHA256 of $HEADER.$PAYLOAD with candidate secrets, and compare to the signature. Tools like JWT-Cracker or hashcat mode 16500 automate this. Any secret short enough to appear in a wordlist is fair game.
Mitigation: use ≥256-bit random secrets, and prefer asymmetric algorithms (RS256/ES256) so the signing key never has to be distributed.
Defenses (summary)
- Pin accepted algorithms server-side; reject
noneand any algorithm you didn’t explicitly configure. - Use strong, high-entropy secrets for HMAC; prefer asymmetric signing when keys cross trust boundaries.
- Validate
exp,nbf,iss, andaudclaims on every token. - Don’t store sensitive data in the payload — it’s base64, not encrypted.
Sources
- jwt.io — Introduction to JSON Web Tokens
- RFC 7519 — JSON Web Token (JWT)
- PortSwigger — JWT Attacks
- OWASP — JSON Web Token Cheat Sheet for Java
Related: burp-suite, xss-attacks, http