This article explains the JWT none algorithm vulnerability through a practical lab exercise where users forge an admin token by manipulating the algorithm header field. The attack demonstrates how servers that allow tokens to declare their own verification method can be bypassed entirely. The vulnerability remains common in real systems because developers often build custom JWT verifiers that trust the token header instead of enforcing a specific algorithm server-side.
- JWT structure and readabilityA JWT consists of three base64url-encoded parts (header, payload, signature) joined by dots. The header names the signing algorithm, the payload contains claims like username and role, and the signature proves the server issued it. Critically, the header and payload are only encoded, not encrypted, so anyone can read them without the secret key.
- The none algorithm bypass mechanismThe JWT standard defines an algorithm called 'none', meaning 'unsigned', which indicates no signature verification should occur. A vulnerable verifier reads this algorithm from the token header and skips signature checking entirely. An attacker can set the algorithm to 'none', change the role claim to 'admin' in the payload, and append a trailing dot with no signature, and the server will accept it as valid.
- Why signature verification is criticalThe signature is the only protection preventing modification of JWT claims. Once an attacker disables signature verification by switching to the 'none' algorithm, every claim in the payload becomes rewritable. The payload's encoding provides no security—only the cryptographic signature proves authenticity.
- Historical pattern of misimplementationIn 2015, researchers demonstrated that several popular JWT libraries accepted 'alg: none' tokens or could be tricked into verifying with wrong key types because they trusted the header. While libraries were patched (including the Node.js jsonwebtoken library tracked as CVE-2015-9235), the mistake persists in hand-written verifiers, old dependencies, and remains a regular bug bounty finding.
- Server-side algorithm pinning defenseThe core defense is that the server must decide which algorithm verifies tokens, not the token itself. Developers should explicitly pass the expected algorithm to the verify function and reject any token whose header disagrees. Additionally, servers must explicitly reject 'none' for authentication tokens, use maintained libraries kept updated, and validate other claims like expiry, issuer, and audience.
- Payload is signed but not secretDevelopers must understand that the payload is encoded for structure and standardization, not for confidentiality. Any sensitive data placed in a JWT payload should not be considered hidden from attackers, since base64url encoding is trivially reversible. Only the signature provides security guarantees.
Bottom line: The JWT none algorithm vulnerability succeeds because vulnerable servers trust the token header to declare verification settings rather than enforcing a single algorithm server-side; the practical defense is to explicitly pin the expected algorithm in your verify function and reject 'none' outright. This vulnerability persists in real systems despite being known since 2015, making it essential for developers to use maintained JWT libraries, keep them updated, and never implement custom token ver
📋 In this walkthrough
What this lab teaches
A JSON Web Token (JWT) is three base64url-encoded parts joined by dots: a header, a payload and a signature. The header names the signing algorithm, the payload carries claims like your username and role, and the signature proves the server issued it.
This lab teaches why a server must decide for itself how tokens are verified, and never let the token decide.
How InvoiceCloud uses JWTs
After you log in as a guest, the lab shows your token. A “Verify Token” control sends a token to the verifier, which reads the algorithm from the token header and checks the signature accordingly. If the payload says your role is admin, the lab marks you solved.
The attack approach
- Decode your token. The header and payload are just base64url-encoded JSON, readable by anyone.
- Look at the algorithm in the header and the role in the payload.
- The JWT standard defines an algorithm called
none, meaning “unsigned”. A safe verifier rejects it; this one does not. - Build a token that says “no signature” and claims the admin role.
Full walkthrough (spoiler)
Stuck after a few attempts? Open the steps below. Solving it yourself first is worth more XP, and more learning.
Show the step-by-step solution
- Copy your token and decode its header and payload. You can use the browser console or any JWT decoder.
- Write a new header that sets the algorithm to
none, and a new payload that keeps your user but sets the role to admin. - Base64url-encode each part: standard base64, then remove the padding and swap the two URL-unsafe characters. The lab’s hints show exactly how.
- Join header and payload with a dot and add a trailing dot with nothing after it, because there is no signature.
- Paste the forged token into Verify Token. The verifier sees “no algorithm”, skips the signature check, reads your admin role and the lab marks you solved.
Why it works
The signature is the only thing that stops anyone editing a JWT. By letting the token choose its own verification method, the server let the attacker switch verification off. The payload was never secret, just signed, so once the signature check is skipped, every claim in it can be rewritten.
Where this happens in the real world
In 2015 researchers showed that several popular JWT libraries accepted alg: none tokens, or could be tricked into verifying with the wrong kind of key, because they trusted the header. Libraries were patched (for example the Node.js jsonwebtoken issue tracked as CVE-2015-9235), but the mistake keeps returning in hand-written verifiers and old dependencies, and JWT flaws remain a regular bug bounty finding.
How to defend against it
- Pin the algorithm. Pass the expected algorithm to your verify call explicitly and reject tokens whose header disagrees.
- Never accept none for authentication tokens.
- Use a maintained library and keep it updated, rather than decoding and checking tokens yourself.
- Keep secrets out of the payload. It is encoded, not encrypted.
- Validate the other claims too: expiry, issuer and audience.
Try it, then go further
Reading about it is the easy part. Open the lab, try your own approach before peeking at the spoiler, and see how many attempts it takes.
