Security

What JWT algorithms are secure?

The secure JWT algorithms are HS256/384/512 (HMAC), RS256 and PS256 (RSA), ES256/384/512 (ECDSA), and EdDSA. Never accept alg:none, never use a weak HMAC secret, and always verify against an explicit algorithm allowlist to block the RS256-to-HS256 confusion attack.

By Inventive HQ Team

The secure JWT signing algorithms are the HMAC family (HS256, HS384, HS512), the RSA families (RS256/384/512 and the PSS variants PS256/384/512), the ECDSA family (ES256, ES384, ES512), and EdDSA — all standardized in RFC 7518 and RFC 8037. Every one of them is safe when used correctly. What actually breaks JWT security is not the choice between them but three specific mistakes: accepting the none algorithm, using a weak or password-derived HMAC secret, and letting the token's own header decide the verification algorithm (the RS256-to-HS256 confusion attack). For a new system, default to ES256 or EdDSA; use HS256 only inside a single trust domain with a strong random secret.

That paragraph is the summary an AI overview will give you. The rest of this article is what it can't: why symmetric and asymmetric algorithms fail in different ways, exactly how the algorithm-confusion attack forges an admin token from a public key, and a decision table that tells you which algorithm to pick for your architecture.

JWT signing algorithm families Three secure families — HMAC (symmetric), RSA and ECDSA and EdDSA (asymmetric) — shown against a danger zone containing alg:none and weak HMAC secrets. JWT algorithms: two safe families, one danger zone Symmetric (HMAC) One shared secret signs + verifies HS256 HS256 / HS384 / HS512 Asymmetric (key pair) Private key signs, public key verifies RS256 ES256 EdDSA also PS256 · ES384/512 · Ed448 Danger zone — never accept alg: none weak HMAC secret

The one decision table

Here is the whole answer in one view: every mainstream JWT algorithm, its family, its key model, when it fits, and its security verdict — followed by the two things you must reject outright.

AlgorithmTypeKey modelWhen to useSecurity verdict
HS256 / HS384 / HS512HMAC (symmetric)One shared secretSingle trust domain; monolith or tightly-coupled services you fully controlSecure only with a ≥32-byte random secret; weak secret = forgeable
RS256 / RS384 / RS512RSA (asymmetric)Private signs, public verifiesDistributed systems; many verifiers; widely-supported defaultSecure at RSA-2048+; large signatures; PKCS#1 v1.5 padding
PS256 / PS384 / PS512RSA-PSS (asymmetric)Private signs, public verifiesSame as RS* where library supports PSSSecure; modern PSS padding, technically superior to RS*
ES256 / ES384 / ES512ECDSA (asymmetric)Private signs, public verifiesNew distributed systems; small tokens, fast verifySecure; ES256 ≈ RSA-3072 strength with tiny keys
EdDSA (Ed25519 / Ed448)Edwards-curve (asymmetric)Private signs, public verifiesNew systems; best modern default where supportedSecure; deterministic, avoids ECDSA nonce pitfalls
alg: none(unsigned)No key at allNever in productionInsecure — anyone can forge any claim
Weak HS secretHMAC (symmetric)Password / short stringNeverInsecure — brute-forceable offline, then forgeable

Quick pick: integrated system you fully control → HS256 with a strong secret. Independent services verifying tokens → ES256 (or RS256 for maximum compatibility). Greenfield with a modern library → EdDSA.

Symmetric algorithms: HMAC variants

HMAC (Hash-based Message Authentication Code) algorithms use a shared secret key for both signing and verifying tokens. The server creates tokens using the secret, and any service with the same secret can verify them. Common HMAC variants are HS256 (HMAC with SHA-256), HS384, and HS512.

HMAC is fast and simple, making it suitable for systems where all components sit in the same trust domain. If you run a monolith or a tightly coupled set of microservices under your control, HMAC works well: the shared secret is easy to manage, and verification is computationally lightweight.

The limitation shows up in distributed systems. Every service that verifies tokens must hold the secret, and the more places that know it, the more chances it leaks — hard-coded into source, left in logs, or stored insecurely. One leaked secret compromises every service that shares it.

The other trap is the secret itself. HS256 provides 256-bit security only if the secret is 256 bits of real randomness. A secret should be at least 32 bytes of cryptographically random data, not a memorable password. A password-derived or short secret can be captured from a single token and brute-forced offline, after which the attacker can mint any token they like. HS384 and HS512 raise the margin at negligible cost, but HS256 with a strong secret is sufficient and widely supported.

Asymmetric algorithms: RSA, ECDSA, EdDSA

Asymmetric algorithms use a key pair: the issuer signs with a private key, and any verifier checks the signature with the corresponding public key. This is what makes distributed verification safe — verifiers never hold signing material, so a compromised verifier cannot forge tokens.

RSA (RS256/384/512). RS256 is the most commonly used asymmetric JWT algorithm. It provides excellent security and lets many services verify tokens without the signing key. Security depends on key size: use RSA-2048 or larger (RSA-2048 ≈ 112-bit strength, RSA-4096 ≈ 128-bit). RSA signatures are large (256 bytes for RSA-2048), so tokens are bigger. PS256/384/512 are the RSA-PSS variants — same RSA math, but the modern probabilistic PSS padding instead of PKCS#1 v1.5. The practical security gain is small, but PS256 is technically superior where supported.

ECDSA (ES256/384/512). ECDSA delivers strong security with much smaller keys. ES256 uses a 256-bit key for roughly 128-bit strength — comparable to RSA-3072 — with smaller signatures (64 bytes) and faster operations. ES512 uses a 521-bit curve (not 512 — a spec quirk) for about 256-bit strength. ES256 is the sweet spot and is increasingly the default for new distributed systems.

EdDSA (Ed25519 / Ed448). Standardized for JWT in RFC 8037, EdDSA is the most modern option: fast, small keys and signatures, and deterministic signatures that sidestep the nonce-reuse failures that have historically broken ECDSA implementations. Where your library supports it, EdDSA is an excellent default.

Advertisement

Algorithms and mistakes to avoid

Some things break JWT security no matter how good the underlying math is.

Never accept alg: none. The none algorithm marks a token as unsigned. If the server accepts it, an attacker takes any token, sets the header to {"alg":"none"}, drops the signature, rewrites the payload to claim admin or another user's identity, and it passes with no cryptographic check whatsoever. Reject unsigned tokens outright and require a valid signature on every request. Most modern libraries disable none by default — but custom and legacy code still falls for it.

Never let the token choose its own algorithm. This is the RS256-to-HS256 algorithm confusion attack, and it is the single most important pitfall on this page:

RS256 to HS256 algorithm confusion attack An attacker flips the header algorithm from RS256 to HS256 and signs a forged token using the server's public RSA key as the HMAC secret; a naive verifier that trusts the header accepts it. Algorithm confusion: the public key becomes the forger's secret Server signs (RS256) private key · publishes public key Attacker rewrites alg: RS256 → HS256 HMAC key = public key Naive verifier trusts header → runs HMAC forged token travels from attacker to verifier The fix Pass an explicit algorithm allowlist to every verify() call. The server decides the algorithm and the key type — never the token header.

The attack works because some libraries read the alg field from the token and pick the verification path accordingly. An attacker changes RS256 to HS256, then signs a forged payload with HMAC-SHA256 using the server's public RSA key as the HMAC secret. The public key is, by design, available to everyone — so the attacker has all they need. A verifier that trusts the header runs HMAC with that same public key, the signature matches, and the forged token is accepted. The fix is to call verify() with an explicit algorithm allowlist (and the correct key type) so the server, not the token, decides how verification happens.

Other things to avoid: HS1 (HMAC-SHA-1) and any SHA-1-based scheme are deprecated — migrate to HS256+. And never invent a custom or proprietary signing scheme; the JWT ecosystem's security comes from well-tested standard algorithms, and homegrown crypto almost always introduces flaws.

Inspect any token before you trust it

Before choosing or debugging an algorithm, it helps to see exactly what a token declares in its header — the alg and kid values are right there in the first Base64URL segment. Decode one to confirm the algorithm your system is actually issuing (and to prove to yourself that the payload is only encoded, not encrypted):

Loading interactive tool...

Remember: decoding shows you the claims but does not verify the signature. A decoder is a debugging aid, not a trust decision — always verify signatures server-side with a pinned algorithm.

Choosing an algorithm for your use case

For small, tightly controlled environments where you own every component, HS256 offers simplicity and acceptable security — the reduced complexity and faster verification can outweigh symmetric key-management concerns, provided the secret is strong and well protected.

For microservices or distributed systems, prefer RS256 or ES256. The ability to distribute public keys without compromising security is the whole point. Between them, ES256 wins on performance and token size; RS256 wins on universal compatibility.

For new implementations, reach for ES256 or EdDSA if your library supports them — they represent current best practice. Legacy interoperability may still require RS256 or HS256, but new systems should lead with the modern options.

For strict performance at massive scale (millions of requests per second), ECDSA's faster verification and smaller signatures can justify the migration effort. For typical applications, the performance difference is negligible and any secure choice is fine.

For compliance-driven environments (finance, healthcare, government), check whether your framework mandates specific algorithms or minimum key strengths before deciding.

Key management: the other half of the job

Algorithm choice is only half of JWT security; key management is the other half. For HMAC, keep the secret in a secret manager, vault, or environment variable — never in source — and rotate it periodically. For asymmetric algorithms, lock down the private key with strict access controls while freely distributing the public key.

Support rotation from day one. Maintain multiple valid public keys during transitions so old tokens stay valid while new ones use the new key, and use the kid (key ID) header to indicate which key signed a token. That single header field is what makes seamless key rotation possible: the verifier reads kid, looks up the matching key, and validates without downtime.

Conclusion

Secure JWT implementations use proven, standardized algorithms: HS256 for symmetric scenarios, RS256/ES256 for asymmetric scenarios, and EdDSA as the modern default where available. Modern systems should prefer ECDSA (ES256) or EdDSA over RSA for superior performance and smaller keys. Then avoid the three failure modes that make any of them insecure: never accept alg: none, never use a weak HMAC secret, and never let the token's header decide the verification algorithm. Pair the right algorithm with disciplined key management and an explicit verification allowlist, and your JWTs will be exactly as strong as the math promises.

Frequently Asked Questions

What JWT algorithms are considered secure?

The secure, standardized JWT signing algorithms are the HMAC family (HS256, HS384, HS512), the RSA families (RS256/384/512 and the newer PSS variants PS256/384/512), the ECDSA family (ES256, ES384, ES512), and EdDSA (Ed25519/Ed448). All are defined in RFC 7518 and are safe when used correctly. The two things that make an otherwise secure algorithm insecure are accepting the "none" algorithm and using a weak HMAC secret. For new systems, ES256 or EdDSA are the best defaults.

Is HS256 or RS256 more secure for JWT?

Neither is inherently stronger at equal, correct settings — they solve different problems. HS256 is symmetric: one shared secret both signs and verifies, which is simple but means every verifying service must hold the secret. RS256 is asymmetric: a private key signs and a freely distributable public key verifies, so verifiers never hold signing material. Use HS256 inside a single trust domain; use RS256 (or ES256) when independent services must verify tokens they cannot mint. The real risk is mixing the two — see the RS256-to-HS256 confusion attack.

Why should you never use the JWT none algorithm?

The "none" algorithm marks a token as unsigned. If a server accepts it, an attacker can take any token, change the header to {"alg":"none"}, delete the signature, rewrite the payload to claim admin rights or another user's identity, and it will pass verification. There is no cryptographic check at all. Production code must reject alg:none outright and require a valid signature on every token. Most modern libraries disable it by default, but custom and legacy implementations still fall for it.

What is the RS256 to HS256 algorithm confusion attack?

It abuses code that lets the token's own header decide the verification algorithm. The attacker changes alg from RS256 to HS256, then signs a forged token using HMAC-SHA256 with the server's RSA public key as the HMAC secret. Because the public key is, by design, public, the attacker has everything needed. A naive verifier reads "HS256", runs HMAC with that same public key, and the signature matches. Prevention: pass an explicit algorithm allowlist to every verify() call so the server, not the token, decides the algorithm.

How strong does an HS256 secret need to be?

An HS256 secret should be at least 32 bytes (256 bits) of cryptographically random data, ideally matching the hash output length. Do not use a human-chosen password or a short string — those are guessable and can be brute-forced or dictionary-attacked offline once someone captures a token, letting them forge new ones. Generate the secret with a secure random source, store it in a secret manager or environment variable, and rotate it periodically. A weak HS256 secret defeats the algorithm no matter how strong the math is.

Should I use ES256 or RS256 for new JWTs?

Prefer ES256 for new systems. ES256 (ECDSA on the P-256 curve) delivers roughly 128-bit security — comparable to RSA-3072 — with far smaller keys, smaller signatures, and faster operations. RS256 remains a solid, universally supported choice and is fine for compatibility with existing infrastructure. If your library supports it, EdDSA (Ed25519) is an even more modern option with excellent performance and fewer implementation footguns than ECDSA.

Is EdDSA supported in JWT?

Yes. EdDSA was standardized for JOSE/JWT in RFC 8037 and uses the Ed25519 and Ed448 curves. It offers strong security, fast signing and verification, small keys and signatures, and deterministic signatures that avoid the nonce-reuse pitfalls that have historically bitten ECDSA implementations. Support is now common in mainstream JWT libraries, and where it exists, EdDSA is an excellent modern default.

What is the difference between RS256 and PS256?

Both use RSA with SHA-256, but they differ in the padding scheme. RS256 uses the older PKCS#1 v1.5 padding; PS256 uses RSA-PSS, a probabilistic padding with stronger, more modern security proofs. In practice the security difference is negligible for typical applications, but PS256 is technically superior. If your JWT library supports PS256 and interoperability allows it, it is a reasonable upgrade over RS256.

Can JWTs be encrypted instead of just signed?

Yes. A signed token is a JWS (JSON Web Signature) and protects integrity and authenticity — anyone can still read the payload. If the payload must stay confidential, use JWE (JSON Web Encryption), which encrypts the content. Most authentication tokens are signed JWS because they carry no secrets, only identity and authorization claims; use JWE when the token itself contains sensitive data. Never put secrets in a plain signed JWT and assume they are hidden — Base64URL is encoding, not encryption.

jwtalgorithmscryptographysecurityencryption