In this lesson, you will learn why storing sensitive information inside a JSON Web Token (JWT) creates a major security vulnerability, regardless of how well you protect your application.
The JWT Illusion
When you first learn about JWTs, it is tempting to treat the "payload" section as a convenient way to pass data around. Because JWTs are Base64URL-encoded, developers often mistakenly assume they are "hidden." However, Base64URL is not encryption—it is simply a formatting scheme. Anyone who intercepts a JWT can decode the payload instantly using simple tools or even a browser console.
The Exposure Risk
If you place sensitive data—such as a user's home address, internal database IDs, or "roles" that might inadvertently reveal backend logic—inside the token, that data is exposed to the client, any middleware logging tools, or a malicious actor who gains access to the token.
Consider this payload:
{
"sub": "12345",
"name": "Jane Doe",
"email": "jane@example.com",
"internal_discount_code": "ADMIN_VIP_99",
"is_admin": true
}
If an attacker intercepts this token, they now have your user’s email and a secret internal discount code. Worse, if your backend relies on the is_admin claim to authorize sensitive actions without checking the database, you have created an easy path for privilege escalation.
Protecting Your Data
The rule is simple: A JWT should be an opaque proof of identity, not a data bag.
- Keep it minimal: Store only the
sub(subject/user ID) and perhaps a smalliat(issued at) orexp(expiration) claim. - Look it up, don't ship it: If your application needs the user's current email or roles, use the
subto perform a quick, efficient query against your database or cache. This ensures the data stays on the server, where it belongs. - Assume public visibility: Treat every bit of information in your JWT payload as if it were printed on a postcard that everyone can read.
Key Takeaway: Never put sensitive data in a JWT payload; treat the token as a public identity card and fetch all private application data from your secure database on every request.