How to Sign a JWT with HS256

By · Published

HS256 (HMAC using SHA-256) signs a token with one shared secret. The issuer and every verifier need the same secret. That's the tradeoff against RS256. It's simpler to set up (one string instead of a keypair), but any service that can verify tokens can also forge them, because signing and verifying use the same key.

What gets signed

The signature is computed over the ASCII bytes of base64url(header) + "." + base64url(payload): the literal encoded string, dot included. So if you re-serialize the JSON with a different key order or spacing after signing, verification fails, even though the payload means exactly the same thing.

signature = HMAC-SHA256(
  base64url(header) + "." + base64url(payload),
  secret
)
Advertisement

The alg field in the header

{ "alg": "HS256", "typ": "JWT" } is the standard header, but once the token leaves your server the alg field is attacker-controlled. Someone can decode a token, change alg to none, strip the signature, and see whether a lazy verifier accepts it. A correct verifier hardcodes the algorithm it expects and never reads alg from the incoming token to decide how to verify it.

Secret length

HMAC-SHA256 accepts a secret of any length, but a short one (under 32 bytes, say) can be brute-forced offline once someone has a valid token to test guesses against. Use a random secret of at least 32 bytes. openssl rand -base64 32 will do if you're generating one for a production service. Something like "secret123" is fine for trying out this tool locally and nowhere else.

Advertisement

Try the JWT Encoder →

Related tools & guides

Part of JWT Tools & Guide