HS256: HMAC with SHA-256 in a JWT
HS256 is a keyed hash over the token, computed with a secret both the issuer and the verifier hold. It is the only algorithm RFC 7518 requires an implementation to support, which is why it is the default nearly everywhere.
At a glance
- Algorithm
- HS256, HMAC using SHA-256
- Registered in
- RFC 7518 section 3.2
- Implementation requirement
- Required, the only algorithm at that level in RFC 7518
- Key
- One shared secret, at least 256 bits, which is 32 bytes of real entropy
- Signature size
- 32 bytes, 43 base64url characters
- Verified in the decoder
- Yes, in your browser. Paste the shared secret, with a checkbox to read that secret as Base64URL rather than text.
In detail
HMAC is symmetric: the same secret makes and checks the signature. That is what makes HS256 quick to adopt and what limits where it belongs. Anyone who can verify a token can also mint one, so the algorithm fits a single service validating tokens it issued itself, and fits badly the moment a second party needs to verify without being trusted to issue.
RFC 7518 requires a key at least as long as the hash output, so 256 bits for HS256. The number that matters is entropy, not characters. A memorable passphrase has far less entropy than its length suggests, and because verification is offline an attacker with a captured token can try candidate secrets as fast as hardware allows, with no rate limit and no log entry. Generate the secret randomly and store it like any other credential.
The classic attack on HS256 is not against the algorithm but against the verifier. Given a service that expects RS256, an attacker rewrites the header to HS256 and signs the token using the RSA public key bytes as the HMAC secret. A verifier that reads the algorithm from the token and hands its configured key material to whichever function the header names accepts it, because the public key is not secret.
The decoder verifies HS256 in your browser using the browser's own cryptography. Paste the token, paste the secret, and tick the Base64URL box when the secret is stored encoded rather than as text, which is a common cause of a correct secret failing to verify. The token and the secret stay on your machine; nothing is uploaded.
Examples
Header: { "alg": "HS256", "typ": "JWT" }
Encoded: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
signature = HMAC-SHA256(secret, base64url(header) + "." + base64url(payload))
token = base64url(header) + "." + base64url(payload) + "." + base64url(signature)The third part is 43 base64url characters for HS256, since the digest is 32 bytes.
Things that catch people out
- Sharing the secret with a service that only needs to verify. It can now issue tokens too, which is exactly what asymmetric algorithms exist to avoid.
- Using a short passphrase because it fits in an environment variable. Offline guessing against a captured token is unlimited, so the secret needs real entropy.
- Storing a Base64URL encoded secret and then verifying against the decoded text, or the other way round. The decoder has a checkbox for this because the mismatch is silent.
Try it: JWT Decoder
Paste a token to decode the header and payload, read the claims and expiry, and verify the signature in your browser. Nothing is uploaded.
JWT Decoder
LOCAL · Browser-OnlyDecode and inspect JSON Web Tokens without sending them to a server
Related JWT claims and algorithms
All jwt claims and algorithms →HS384: HMAC with SHA-384 in a JWT
HS384 signs a JWT with a shared secret using HMAC and SHA-384. How it differs from HS256, why SHA-384 is a truncated SHA-512, and verification support.
RS256: RSASSA-PKCS1-v1_5 with SHA-256 in a JWT
RS256 signs a JWT with an RSA private key and verifies with the public key. Key sizes, the RS256 to HS256 confusion attack, and verifying in your browser.
alg (Algorithm) Header Parameter in a JWT
The alg header names the algorithm used to sign a JWT. Why the accepted algorithms belong in configuration, and what the decoder does with the value.
HS512: HMAC with SHA-512 in a JWT
HS512 signs a JWT with a shared secret using HMAC and SHA-512. Secret sizing, the token size cost, and how the decoder verifies one in your browser.
RS384: RSASSA-PKCS1-v1_5 with SHA-384 in a JWT
RS384 signs a JWT with RSA and SHA-384. What changes against RS256, what does not, and why the token size stays the same for a given key.
RS512: RSASSA-PKCS1-v1_5 with SHA-512 in a JWT
RS512 signs a JWT with RSA and SHA-512. Why the signature length follows the key rather than the hash, and what the decoder needs to verify one.
Tools for this
- JWT Decoder Free online JWT decoder - decode and inspect JSON Web Tokens without sending them to a server
- Hash Generator Free online hash generator - generate MD5, SHA-1, SHA-256 hashes from any input text