tooldura

Developer Tools

Signing a JWT: Which Algorithm, and How Long the Key Has to Be

T
tooldura editorial
8 min readUpdated August 18, 2026Open tool →

A JWT signed with the secret `secret` verifies perfectly. Every library accepts it, every test passes, and the token is worthless: anyone who receives one can recover that key on a laptop in under a second and then mint tokens claiming to be anybody. The algorithm was never the weak part.

Two Families, and the Question That Separates Them

RFC 7518 defines the algorithms a JWT may be signed with, and they fall into two groups that differ in one respect: whether verifying a token also lets you create one.

The HS family is HMAC. One secret signs and the same secret verifies, so anything able to check your tokens is equally able to issue them. Inside a single service this is a non-issue and the simplicity is real.

The RS, PS and ES families use a key pair. The issuer keeps the private half and signs with it; verifiers get the public half, which lets them check signatures and nothing else. The moment a token is read by anything you do not fully control — another team's service, a mobile client, a partner's gateway — this is the property you need, because handing out an HMAC secret to a verifier is handing out the ability to forge.

That single question decides the family. The choice within a family is mostly about signature size and how much you trust the surrounding ecosystem.

What Each Algorithm Costs You

Signature sizes are for the raw bytes before base64, which adds roughly a third.

AlgorithmUnderlying primitiveSignature sizeReach for it when
HS256HMAC with SHA-25632 bytesOne service both issues and verifies
RS256RSASSA-PKCS1-v1_5, SHA-256256 bytes at 2048-bitVerifiers are external; widest library support
PS256RSASSA-PSS, SHA-256256 bytes at 2048-bitSame as RS256 but you want the modern padding
ES256ECDSA on P-256, SHA-25664 bytesAsymmetric, and token size matters
ES512ECDSA on P-521, SHA-512132 bytesRarely; note the curve is P-521, not P-512

The Key Length Rule Nobody Reads

RFC 7518 section 3.2 is one sentence and it is not advisory: a key of the same size as the hash output, or larger, MUST be used with HMAC. That is 32 bytes for HS256, 48 for HS384 and 64 for HS512.

The reason is that an HMAC secret is uniquely exposed to offline attack. Everything an attacker needs to test a guess is inside a token they already hold: take the header and payload, compute the HMAC with a candidate secret, compare it to the signature. No server is involved, so there is no rate limit, no lockout and no log entry. A GPU works through billions of candidates a second, and a wordlist gets there long before brute force does.

Once the secret falls, the attacker does not merely read tokens. They issue them, with any sub, any role and any expiry they like, and every one verifies correctly because it genuinely is correctly signed.

This is also why a passphrase makes a poor JWT secret even when it is long. Thirty characters of English has far less entropy than thirty random bytes, and the attacker is guessing phrases, not characters. Generate the key from a cryptographic random source and store it as Base64.

🔑

Rotation needs a header parameter, not a deployment window

Changing an HMAC secret invalidates every token in circulation at once, which is why so many secrets never change. The way out is kid: put a key id in the header, keep the previous key active for verification while the new one signs, and retire the old key after the longest token lifetime has passed. Without a kid, rotation means logging everybody out simultaneously.

What Goes Wrong When You Mint Your Own

The signing call is the easy part. These are the details that make a token behave oddly in a way the error message never explains.

1

exp written in milliseconds

RFC 7519 defines it as seconds since 1970. A value straight from `Date.now()` is a thousand times too large and puts the expiry roughly fifty thousand years away, so the token never expires and nothing warns you.

2

No aud, or an aud nobody checks

A token minted for one service and accepted by another is a valid token used in a place it was never meant for. Name the audience when signing, and refuse tokens whose audience is not yours when verifying. Both halves are needed for either to matter.

3

A lifetime measured in months

A JWT cannot be recalled: it is valid until it expires, because verification is arithmetic and consults nothing. Long-lived access tokens turn any leak into a long-lived compromise. Keep them to minutes and use a refresh token, which lives in a store you can actually delete from.

4

Claims the client is trusted to have sent

Anything you sign becomes authoritative. Copying a role or a tenant id from a request body into a token, without checking it against your own records first, signs the attacker's assertion for them.

5

The secret in the repository

It is a credential with the power to impersonate every user. Committed once, it is in the history until the history is rewritten, and any fork carries it too.

Mint a signed token to test with

Your claims, a chosen lifetime, HMAC signing that stays in the browser.

Open the JWT Generator →

When a JWT Is the Wrong Tool Entirely

The property that makes JWTs attractive is that a verifier needs no database. That same property is the one that hurts.

A session id in a store can be revoked in one write. A JWT cannot: nothing consults anything, so a token stays good until exp. Password changed, account suspended, device stolen, employee gone on Friday afternoon — none of it reaches a token already issued. Teams discover this, add a revocation list, and check it on every request, at which point they have a database lookup per request and a session id would have been simpler and smaller.

So the honest rule is about distance. Stateless tokens earn their place across a trust boundary: between services, out to mobile clients, into APIs owned by other people, where a shared session store is not realistic. For an ordinary server-rendered web application talking to its own database, a session cookie is smaller, revocable, and has fewer ways to be got wrong.

RFC 8725 exists because so much of the industry adopted the format before working out which of these situations it was in.

Frequently Asked Questions

Related Tools

Keep Reading