JWT Security: How to Read a Token and Catch the Red Flags

Quick answer: No, a JWT is not encrypted; it is signed. Anyone holding a token can decode the payload in seconds. Security depends on server-side verification: you must reject alg: none, pin your algorithm, check the exp claim, use strong secrets, and store tokens in httpOnly cookies to prevent theft.

By Daniel A · Kraftwire Software

· 9 min read

No, a JWT is not encrypted; it is base64url-encoded and signed. Anyone holding a token can decode the header and payload in seconds to read your data, so security depends entirely on server-side verification: you must reject alg: none, pin your signing algorithm, check the exp claim, use long random secrets, and store tokens in httpOnly cookies to prevent XSS theft.

Is a JWT Encrypted or Just Signed?

A JSON Web Token (JWT) is not encrypted. It is signed. This is the most common misconception in modern web development, especially among those using AI tools like Cursor or Windsurf to generate authentication logic. Because a JWT looks like a string of random characters, many developers assume the data inside is hidden. In reality, anyone who holds one of your tokens can read every field inside it in about ten seconds.

The security of a JWT comes entirely from how it is signed and verified, not from the fact that it looks like gibberish. If your vibe-coded app puts sensitive data in the payload, accepts unsigned tokens, or fails to check the expiration timestamp on the server, you have a critical vulnerability. This guide explains how to read a token by hand and the specific red flags you must catch to keep your application secure.

What Is a JWT, Actually?

A JSON Web Token (defined in RFC 7519) is a compact, URL-safe means of representing claims to be transferred between two parties. In a stateful model, authentication might rely on server-side session management, but JWTs allow for a stateless approach where the token itself contains the necessary user information. It consists of three parts separated by dots:

  • The Header: Contains metadata about the token, such as the type (JWT) and the signing algorithm used (e.g., HS256 or RS256).
  • The Payload: Contains the "claims." These are statements about an entity (typically, the user) and additional data.
  • The Signature: Created by taking the encoded header, the encoded payload, a secret, and the algorithm specified in the header, and signing that.

Each of the first two parts is a JSON object that has been base64url-encoded. Base64url is an encoding, not encryption. It exists to make JSON safe to put in a URL or an HTTP header, and it is trivially reversible by anyone. There is no key involved in the decoding process. If you can copy the token, you can read the header and payload.

How Do You Read a JWT in Ten Seconds?

You can inspect a JWT using your browser's developer tools. Look under the Application tab, then check Local Storage or Cookies. Once you have the string, split it at the dots. Take the first chunk and run it through a base64url decoder. You will see something like this:

That is the header. It tells the server which algorithm was used to sign the token. Now decode the second chunk (the payload):

This is the payload. The third chunk is the signature. Do not try to decode it; it is raw bytes, not JSON. While you can do this in a terminal, our free JWT Debugger decodes all three parts entirely in your browser (nothing is sent to a server) and automatically flags the risks described below.

What Are the Biggest JWT Security Red Flags?

When you decode a token, look for these specific red flags.

1. The alg is "none" or the token is unsigned

The JWT specification allows an algorithm value of none. This means the token carries no signature at all. While intended for internal use cases where security is handled elsewhere, it is a catastrophic flaw if your public-facing server accepts it. An attacker can simply change their role to admin in the payload, set the header to "alg": "none", and remove the signature. If your backend doesn't explicitly reject this, they have full access.

2. Algorithm Confusion (HS256 vs RS256)

There are two primary signing families:

  • HS256 (Symmetric): The same secret key is used to both sign and verify the token.
  • RS256 (Asymmetric): A private key signs the token, and a public key verifies it.

In an "algorithm confusion" attack, an attacker takes your public RS256 key (which is often publicly accessible) and uses it as an HMAC secret to sign a forged token with the HS256 algorithm. If your server-side library trusts the header's algorithm choice, it will use the public key to "verify" the HMAC signature and let the attacker in. The fix is to pin the expected algorithm (e.g., RS256 only) in your server code.

3. Sensitive Data in the Payload

Because the payload is only base64url-encoded, it is readable by anyone. Never put passwords, credit card numbers, or internal API keys in a JWT. Treat the payload like a postcard.

4. Missing or Far-Future Expiration (exp)

The exp claim is a Unix timestamp. If it is missing, the token may be valid forever. As noted in recent security discussions, the server can't tell if a token is stolen; it only checks the signature and the expiration. If a token is stolen and has no expiration, the attacker has a permanent key to that user's account. Access tokens should generally last minutes to an hour, supported by a refresh token mechanism.

5. Weak HMAC Secrets

If you use HS256, your security is only as strong as your secret. If your AI-generated code uses a default like my_secret_key or env_secret, it can be brute-forced offline. Once an attacker cracks the secret, they can mint their own tokens for any user. Always use a long, cryptographically random secret stored in an environment variable. For more on this, see our guide on environment variables security.

6. Storing Tokens in localStorage

Where you store the token matters. localStorage is accessible to any JavaScript running on your page. If your site has a single Cross-Site Scripting (XSS) vulnerability, an attacker can steal every user's JWT. Using httpOnly cookies prevents JavaScript from accessing the token, providing a critical layer of defense against XSS-based token theft.

The Ultimate JWT Security Checklist

If you are building with tools like Lovable, Bolt.new, or Replit, use this checklist to audit your auth flow:

  • Server-Side Pinning: Force the server to use a specific algorithm (e.g., RS256) and ignore the alg header sent by the client.
  • Strict Validation: Ensure the server re-verifies the signature and the exp claim on every single request.
  • Payload Hygiene: Only include the bare minimum (e.g., user_id, role).
  • Secure Storage: Use httpOnly, Secure, and SameSite cookie attributes.
  • Short Lifespans: Keep access tokens short-lived (5-15 minutes) to minimize the window of opportunity for stolen tokens.
  • Secret Management: Use 32-byte (256-bit) random strings for HMAC secrets and never commit them to version control.

How SimplyScan Helps

Vibe-coding allows for rapid shipping, but it often skips the rigorous security checks required for production-grade auth. These often include exposed secrets or misconfigured JWT handling.

SimplyScan provides a vibe coding security checklist and an automated scanner that detects these red flags in seconds. Our free scan grades your site across 8 dimensions, including security and GDPR compliance signals, helping you catch broken auth before it becomes a breach.

Wrapping Up

JWTs are a powerful tool for stateless authentication, but they are frequently misunderstood. By decoding your own tokens and checking for these red flags, you can significantly harden your application. Start by using our JWT Debugger to see exactly what your tokens are broadcasting to the world.

When you're ready for a comprehensive audit, run a full scan at simplyscan.io. We’ll check your headers, your secrets, and your architecture to ensure your AI-built app is as secure as it is fast.

Related Free Tools

Browse all 60 free tools.

FAQ

Can someone hack my app if they see one of my JWTs?

If they see a JWT, they can read all the data in the payload (like user IDs or roles). However, they cannot forge a new token unless your signing secret is weak or your server accepts the alg: none exploit. The danger is that if the token is stolen, the attacker can impersonate that user until the token expires.

Is decoding a JWT the same as verifying it?

No. Decoding simply translates the base64url string back into readable JSON; anyone can do this. Verifying is a cryptographic process where the server uses a secret key to ensure the token hasn't been tampered with and checks that the expiration date hasn't passed. Never trust a decoded token that hasn't been verified.

Should I store JWTs in localStorage or cookies?

You should prefer httpOnly cookies with Secure and SameSite flags. localStorage is vulnerable to Cross-Site Scripting (XSS) attacks because any script on the page can read it. httpOnly cookies are inaccessible to JavaScript, making it much harder for an attacker to steal the session token even if they find an XSS bug.

How long should a JWT access token last?

Access tokens should be short-lived, typically between 5 and 60 minutes. Because the server can't easily "revoke" a JWT without extra infrastructure (like a denylist), a short lifespan limits the damage if a token is stolen. Use refresh tokens to allow users to stay logged in without needing long-lived access tokens.

What is the difference between HS256 and RS256?

HS256 is a symmetric algorithm where one secret is used for both signing and verifying. RS256 is asymmetric, using a private key to sign and a public key to verify. RS256 is generally preferred for larger systems because the verification key can be shared publicly without compromising the ability to create new tokens.

Is it safe to put a user's email or role in a JWT payload?

Yes, it is common to put a user's ID, email, or role in the payload so the backend knows who is making the request. However, you must never put sensitive data like passwords, home addresses, or private API keys there, as anyone who intercepts the token can read that information instantly.

Frequently asked questions

Can someone hack my app if they see one of my JWTs?

If an attacker intercepts a JWT, they can read all payload data (like user IDs). They can only "hack" the app by forging tokens if your secret is weak or you accept the 'alg: none' exploit. However, a stolen token allows them to impersonate the user until it expires.

Is decoding a JWT the same as verifying it?

No. Decoding just reverses base64url encoding to make the JSON readable; anyone can do it. Verifying is a cryptographic check where the server uses a secret key to prove the token is untampered and hasn't expired. A backend that decodes without verifying is a major security hole.

Should I store JWTs in localStorage or cookies?

Prefer httpOnly cookies with Secure and SameSite attributes. localStorage is readable by any JavaScript on the page, meaning an XSS vulnerability can lead to total token theft. httpOnly cookies are hidden from JavaScript, providing a vital defense-in-depth layer against session hijacking.

How long should a JWT access token last?

Access tokens should be short-lived, typically 5 to 60 minutes. Since JWTs are stateless, they are hard to revoke instantly; a short expiration limits the window of misuse for a stolen token. Use refresh tokens to maintain the user session securely over longer periods.

What is the difference between HS256 and RS256?

HS256 uses a single shared secret for both signing and verification. RS256 uses a private/public key pair. RS256 is more secure for distributed systems because the public key can be shared for verification without allowing others to create (sign) new tokens.

Is it safe to put a user's email or role in a JWT payload?

It is safe to include non-sensitive identifiers like a user ID or role. However, never include passwords, PII (like home addresses), or secrets. Treat the JWT payload like a postcard: assume anyone who handles it can read exactly what is written on it.

Related guides

  • Base44 Security Guide: Critical Vulnerabilities and How to Protect Your App · To secure a Base44 application, you must manually configure the entity permissions panel for every database table and move all secret API keys to server-side environment variables. Implementing Row-Level Security (RLS) and server-side authorization guards is critical to prevent unauthorized data access and account takeovers in AI-generated apps.
  • Bolt.new Security Guide: 7 Vulnerabilities to Fix Before Launch · Bolt.new apps often ship with critical flaws like API keys bundled in client JavaScript and missing Supabase RLS policies. To secure your app, move secrets to server-side functions, scope RLS to auth.uid(), and enforce server-side authentication. SimplyScan finds these vulnerabilities in 30 seconds, helping you ship safely.
  • Bubble Security Guide: Privacy Rules, API Tokens, and Data Exposure · Bubble apps are not secure by default. Without privacy rules, your database is publicly accessible via the Data API. To secure your app, you must implement Row-Level Security, protect 32-character API tokens, and authenticate backend workflows. SimplyScan's free audit helps detect these risks in ~30 seconds.
  • Content Security Policy for Vibe-Coded Apps: A Practical CSP Guide · Content Security Policy (CSP) is a browser-enforced allowlist that blocks unauthorized scripts, providing the strongest defense against XSS. Most vibe-coded apps ship with no CSP or use 'unsafe-inline', which negates protection. This guide explains how to implement strict policies using nonces, hashes, and report-only mode to secure AI-built applications.

All security guides · Free security tools · Platform scanners · Security checklist