JSON web token format: $HEADER.$PAYLOAD.$SIGNATURE
, where each substring is (URL-safe) base64 encoded. These can be passed around as a user cookie, HTTP header, or queried from local storage.
$HEADER
and $PAYLOAD
are both JSON blobs, but $SIGNATURE
is binary data.
$HEADER
takes the form{ "alg": "RS256", "typ": "JWT" }
, wherealg
is a signing algorithm supported by the server.$PAYLOAD
is a JSON blob containing various pieces of user information, which can include permissioning data. See jwt.io for a detailed breakdown.$SIGNATURE
is the signature (usingalg
from the$HEADER
) of$HEADER.$PAYLOAD
(both parts base64 encoded) using a server-side secret (often an SSL key… but sometimes just a string!).
Use basenc (basenc --base64url
and basenc -d --base64url
) to encode/decode URL-safe base64, rather than the base64 binary. Be sure to strip the trailing =
signs!
“None” signature attacks
Sometimes servers will also support the NONE signature type, which indicates that no signing is used (so the JWT is then just $HEADER.$PAYLOAD.
— note the trailing dot!). If the server allows the NONE signing method, then it’s often possible to just arbitrarily edit the $PAYLOAD
to gain access to other users.
The base64-encoded version of {"typ":"JWT","alg":"none"}
is eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0
.
Public key attacks
JWT algorithms can use a server’s public key if alg
is HS256
. If the public half of the keypair used to sign the JWT is available somehow (for example, if it’s been re-used as the server’s HTTPS certificate), then we can harvest it and use it to forge new JWTs.
The base64-encoded version of {"typ":"JWT","alg":"HS256"}
is eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9Cg
.
Use the following to generate a signature with the above $HEADER
and the PEM-formatted $PUBLIC_KEY_FILE
half of the public/private key to validate the JWTs (when alg
is RS256
):
Brute-forcing weak secrets
If a weak secret (a simple string) is used to sign the JWT token, then it is sometimes possible to brute-force it using JWT-Cracker.