May 15, 2026
JWT JSON Web Token
What is JWT
Beryl
5 min read
Is an open standard that defines a compact and self-contained way to securely transmit information between parties as a JSON object.
Think of it like this: You show your passport (username/password) to the airline staff (the server). Once they verify who you are, they give you a boarding pass (JWT). This pass contains your name, seat number, and flight time. Crucially, it has an official stamp from the airline to prove it's real. When you want to get on the plane, you don't show your passport again. You just show the boarding pass. The security guard doesn't need to call the airline to check if you're allowed in they just look at the stamp to see if it's authentic.
The anatomy of JWT
JWT is composed of three parts separated by dots(.), all Base64-encoded parts: Header. Payload.Signature
- Header: has two parts
{
"alg": "HS256",
"typ": "JWT"
}{
"alg": "HS256",
"typ": "JWT"
}The token type is definitely set to JWT. This helps the application distinguish between different types of tokens when dealing with multiple of them.
The algorithm denoted by alg; is the most critical piece of information in the header. It specifies the cryptographic algorithm that uses secure tokens.
- Payload
{
"sub": "user_12345",
"name": "John Doe",
"admin": true,
"iat": 1738656000,
"exp": 1738659600
}{
"sub": "user_12345",
"name": "John Doe",
"admin": true,
"iat": 1738656000,
"exp": 1738659600
}The payload contains the actual data, known as claims. This is mostly about the user or the session. The claims are categorized into three:
- Registered claims. This is not recommended but required. They are usually there letters long;
iss- Issuer: who created the token
sub- Subject; the user id or unique identifier
aud- audience- who the token is intended for.
exp- expiration- when the token becomes invalid
iat -issued at- when the token was created.
- Public claims: They are custom names you define, often following the IANA JSON Web Token Registry to avoid collision.
- Private Claims: Are custom claims created specifically to share information between parties that agree on using them.
Signature
The signature acts as a security seal that makes JWT trustworthy. While anyone can read the Header and Payload, only someone with the correct key can create or verify the Signature.
HMAC_SHA256(
secret,
base64urlEncoding(header) + '.' +
base64urlEncoding(payload)
)HMAC_SHA256(
secret,
base64urlEncoding(header) + '.' +
base64urlEncoding(payload)
)How JWT works
Where to find JWT in the browser:
The role of JWT in cybersecurity
Stateless authentication: It reduces the attack surface on the server's database and prevents session fixation attacks because JWT contains all the user info and is digitally signed; the server can verify the user's identity simply by looking at the token, without querying a database.
Granular authorization: It facilitates Least Privilege Access. A microservice doesn't need to ask a central authority if a user has permission; it just validates the "scopes" inside the signed token.
Single Sign-on (sso): It centralizes identity management. Users don't have to share passwords with multiple third-party apps, reducing the risk of credential theft.
Data Integrity. It ensures Data Integrity. Even if an attacker intercepts the token, they cannot change the contents without the server knowing. An Identity Provider (IdP) signs a JWT and gives it to the user. The user then presents that same token to a completely different service. As long as that service trusts the IdP's public key, it can verify the user.
Risks
- Algorithm Confusion
If the server uses RS256, a symmetric encryption to sign tokens with a private key and verify them with a public key. An attacker can change the header from RS256 to HS256 (Symmetric). They then sign a fake token using the server's public key as if it were a secret key. This results in the server seeing HS256; the server will use its public key to verify the signature. The math checks out, and the attacher is logged in as an admin.
2. The none algorithm attack
Some older libraries allow the header to specify :
“alg": “none” “alg": “none”The attacker can change the payload to:
"admin" : true"admin" : true, This sets the algorithm to none and deletes the signature entirely. This results in the server being poorly configured. ut accept the token as "already verified" and grant access.
3. Weak secret keys
In other word brute force. Since JWTs are public, an attacker can take a valid token offline and run millions of guesses per second using brute-forcing tools such as hashcat to find the secret key. If the key is a simple word like password123 or secretThe attacker will find it, allowing them to forge their own tokens indefinitely.
4. Token Theft via XSS and CSRF
The biggest risk often isn't the token itself, but where it is stored.
- XSS (Cross-Site Scripting): If a token is stored in Local Storage, an attacker can inject a malicious script into your site to steal the token and send it to their own server.
- CSRF (Cross-Site Request Forgery): If the token is stored in a Cookie without the
SameSiteorHttpOnlyflags, an attacker can trick a user's browser into sending the token to a malicious destination.
5. Information Leakage
Because the Payload is only Base64URL encoded and not encrypted, any sensitive data inside it is effectively public. An attacker intercepts the token via a man-in-the-middle attack or by looking at browser history and decodes the payload to find PII or internal system secrets.
6. Lack of Revocation
Since JWTs are stateless, the server doesn't check a database to see if they are still valid; it only checks the signature and expiration. If a user logs out or their account is deleted, their JWT might still be valid until it hits its expiration date. An attacker who stole that token has a "golden ticket" that works even after the user tries to secure their account.
Mitigating the risks
1. Defending Against Header & Algorithm Attacks
The most dangerous attacks happen when the server trusts the user's input in the Header.
- Hardcode the Algorithm: Never let the
algfield in the JWT header decide which algorithm your server uses. In your backend code, explicitly define the algorithm (e.g.,algorithm: 'RS256'). - Disable "None" Algorithm: Ensure your JWT library is updated and explicitly configured to reject tokens where
"alg": "none". - Use Asymmetric Signing (RS256/ES256): Using a Private Key to sign and a Public Key to verify is inherently more secure than sharing a single secret key (HS256), as it prevents Algorithm Confusion and protects the signing key.
2. Securing Token Storage (Preventing Theft)
Where you store the token on the client side determines how easily it can be stolen.
- Use
HttpOnlyandSecureCookies: Do not store JWTs inlocalStorage. If you store them in a cookie with theHttpOnlyflag, JavaScript cannot access it, which completely mitigates XSS-based token theft. - SameSite Cookie Attribute: Set your cookie to
SameSite=StrictorSameSite=Laxto prevent CSRF (Cross-Site Request Forgery) attacks.
3. Strengthening the Secret Key
If you must use a symmetric algorithm (HS256), the key is your only line of defense against brute force.
- Entropy Matters: Use a long, random string generated by a cryptographically secure random number generator.
- Key Rotation: Change your signing keys periodically. This limits the "shelf life" of any potentially leaked keys.
- Environment Variables: Never hardcode secrets in your source code. Use protected environment variables or a Secret Management service (like AWS Secrets Manager or HashiCorp Vault).
4. Handling Token Lifespan & Revocation
Since JWTs are stateless, you need a way to "kill" a token if a user's phone is stolen or they log out.
- Short-Lived Access Tokens: Set the
exp(expiration) claim to something short, like 15 minutes. - The Refresh Token Pattern: Issue a short-lived Access Token (JWT) and a long-lived Refresh Token (stored in a database). This allows you to revoke access by simply deleting the Refresh Token from your database.
- Implement a Blacklist/Denylist: For high-security apps, keep a fast-access list (like in Redis) of "logged out" token IDs (
jticlaim) that the server should reject even if they haven't expired yet.
Payload Hygiene
- Zero Sensitive Data: Assume the payload is public. Only include non-sensitive identifiers like a UUID or specific user roles.
- Audience and Issuer Validation: Always check the
aud(Audience) andiss(Issuer) claims. This ensures a token intended for your "Mobile App" isn't being used to gain access to your "Admin Dashboard."