Security
11 min read
286 views

JWTs Aren't Encrypted: The #1 Misconception That Leads to Data Leaks

IT
InstaTunnel Team
Published by our engineering team
JWTs Aren't Encrypted: The #1 Misconception That Leads to Data Leaks

JWTs Aren’t Encrypted: The #1 Misconception That Leads to Data Leaks

When developers first encounter JSON Web Tokens (JWTs), they often make a dangerous assumption: the token looks like encrypted gibberish, so the data inside must be secure. This misconception has led to countless data breaches, exposed personally identifiable information (PII), and severe security vulnerabilities across applications worldwide. The uncomfortable truth is that standard JWTs are about as secure as writing sensitive information on a postcard and mailing it through the postal system—anyone who intercepts it can read every word.

The Fundamental Misunderstanding

JSON Web Tokens have become the de facto standard for authentication in modern web applications. They’re compact, self-contained, and can carry information about users and permissions. However, the very features that make JWTs convenient also make them frequently misunderstood. The critical misconception stems from how JWTs appear to developers: a long string of seemingly random characters separated by dots. This appearance creates a false sense of security that leads developers to store sensitive data directly in the token payload.

The reality is far less secure than most developers realize. A standard JWT consists of three parts separated by periods: the header, the payload, and the signature. While the signature provides integrity verification through cryptographic signing, the header and payload are merely Base64URL encoded—not encrypted. Base64 encoding is a reversible transformation designed to represent binary data in ASCII string format, not a security mechanism. Anyone with basic programming knowledge can decode a JWT in seconds.

Understanding Base64 Encoding vs. Encryption

To understand why this matters, we need to distinguish between encoding and encryption. Base64 encoding is a simple, reversible transformation that converts data into a specific character set. It’s designed for data transmission and storage compatibility, not security. Every programming language includes built-in functions to encode and decode Base64 with a single line of code. No password, key, or secret is required to reverse the process.

Encryption, on the other hand, is a cryptographic process that transforms data using a secret key, making it unreadable without that key. True encryption provides confidentiality—even if someone intercepts encrypted data, they cannot read it without the decryption key. Standard JWTs signed with algorithms like HS256 or RS256 are not encrypted. They are signed, which is fundamentally different.

The signature in a JWT serves one purpose: to verify that the token hasn’t been tampered with and was issued by a trusted source. When a server creates a JWT, it generates a signature by hashing the encoded header and payload along with a secret key. When the server receives the token back, it recalculates this signature to verify authenticity. However, this signature does nothing to hide the contents of the payload. Anyone can read the payload; they just can’t modify it without invalidating the signature.

How Easy Is It to Decode a JWT?

Disturbingly easy. Let’s demonstrate with a typical JWT you might encounter in a web application:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsInNzbiI6IjEyMy00NS02Nzg5Iiwic2FsYXJ5Ijo4NTAwMCwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

To decode this token, you don’t need specialized tools or hacking skills. You can visit jwt.io, a popular JWT debugging website, paste the token, and instantly see its contents. Alternatively, you can write a simple script in any programming language. In JavaScript, it’s literally one line:

JSON.parse(atob(token.split('.')[1]))

In Python, it’s equally simple:

import base64, json
json.loads(base64.b64decode(token.split('.')[1] + '=='))

The decoded payload from our example token would reveal:

{
  "userId": "1234567890",
  "email": "john.doe@example.com",
  "ssn": "123-45-6789",
  "salary": 85000,
  "iat": 1516239022
}

This example illustrates a catastrophic security failure. The token contains a Social Security number, salary information, and other personally identifiable information that should never be transmitted in a readable format. Yet this scenario plays out in production systems more often than the security community would like to admit.

Real-World Impact: Data Breaches from JWT Misuse

The consequences of treating JWTs as encrypted containers are not theoretical. Security researchers and penetration testers regularly discover applications leaking sensitive data through JWT payloads. Common examples include:

Healthcare applications storing patient medical record numbers, diagnosis codes, or insurance information in JWTs. A malicious actor intercepting these tokens could access protected health information without ever breaking into the database.

Financial services platforms including account balances, transaction histories, or internal user IDs that map to account numbers. These tokens, captured through network interception or client-side JavaScript access, expose financial data directly.

E-commerce sites embedding credit card last-four digits, shipping addresses, or purchase histories in tokens. While developers might think partial card numbers are safe, combined with other leaked information, they contribute to identity theft.

Internal enterprise applications placing employee IDs, salary bands, performance ratings, or organizational hierarchy information in JWTs. These tokens, if exposed through browser developer tools or network logs, can reveal confidential corporate information.

Recent vulnerabilities in 2025, such as CVE-2025-2079 and CVE-2025-20188, involved hard-coded JWT secrets that allowed attackers to generate valid tokens, but the fundamental issue extends beyond secret management. Even with properly secured signing secrets, the readable nature of JWT payloads presents an inherent risk when developers store sensitive information within them.

The Anatomy of a JWT: What’s Actually Protected?

A JWT consists of three components, each serving a specific purpose:

The Header (Base64URL encoded) specifies the type of token and the signing algorithm used. It typically looks like:

{
  "alg": "HS256",
  "typ": "JWT"
}

The Payload (Base64URL encoded) contains the claims—statements about an entity and additional data. This is where developers often make critical mistakes, storing sensitive information they believe is protected.

The Signature (cryptographically generated) verifies token integrity. For HS256, it’s created by hashing the encoded header and payload with a secret key using HMAC-SHA256.

Only the signature provides any security property, and that property is integrity, not confidentiality. The signature proves the token hasn’t been modified and was issued by someone with the secret key. It does not hide the payload contents from prying eyes.

What Should Actually Go in a JWT?

If sensitive data should never appear in JWT payloads, what should go there? The answer is non-sensitive identifiers and metadata that help the server validate requests and maintain stateless authentication.

Appropriate JWT payload contents include:

  • User ID or username (non-sensitive identifiers)
  • Token expiration time (exp claim)
  • Issued-at timestamp (iat claim)
  • Issuer identification (iss claim)
  • Subject identification (sub claim)
  • Audience specification (aud claim)
  • Non-sensitive user roles or permissions
  • Session identifiers

The key principle is that anything in the payload should be information you’d be comfortable displaying in application logs or server console output. If you wouldn’t want that data appearing in a log file someone else might read, it doesn’t belong in a JWT payload.

For sensitive information, maintain server-side storage with the JWT containing only a reference identifier. When the server receives the token, it extracts the user ID or session identifier, then looks up additional information from a secure database. This approach maintains the benefits of stateless authentication while protecting sensitive data.

The Alternative: Opaque Tokens for Session Management

Many security experts argue that for session management, opaque tokens provide better security characteristics than JWTs. An opaque token is a randomly generated string that serves as a lookup key for server-side session data. Unlike JWTs, opaque tokens reveal nothing about the user or session—they’re truly meaningless to anyone who intercepts them.

When implementing opaque tokens, the server generates a cryptographically random string, associates it with the user’s session data in a secure storage system (like Redis or a database), and returns the token to the client. On subsequent requests, the client sends the token, and the server looks up the associated session data. If the token doesn’t exist in storage or has expired, the request is rejected.

This approach offers several security advantages. First, token contents are never exposed because there are no contents—only a random identifier. Second, sessions can be immediately revoked by removing the server-side record, something impossible with stateless JWTs. Third, sensitive session data never leaves the server environment.

The tradeoff is increased server-side storage requirements and database lookups on every request. For many applications, especially those handling sensitive data, this tradeoff is worthwhile. The security benefits outweigh the minimal performance overhead.

When JWTs Are Appropriate

Despite these concerns, JWTs do have legitimate use cases where they provide genuine advantages. They excel in scenarios requiring stateless authentication across distributed systems, such as microservices architectures where centralized session storage would create bottlenecks.

JWTs work well for API authentication where the token carries only non-sensitive identifiers and the API gateway can verify signatures without database lookups. They’re useful for single sign-on (SSO) systems where multiple applications trust the same issuer and need to verify user authentication independently. They’re appropriate for short-lived access tokens in OAuth 2.0 flows where expiration times are measured in minutes and sensitive operations require additional verification.

The key is understanding that JWTs provide authentication and integrity verification, not confidentiality. When used properly—with minimal payload data and appropriate security controls—they serve their purpose well. The problems arise when developers misunderstand their security properties and treat them as encrypted containers.

JWE: When You Actually Need Encryption

For scenarios requiring encrypted token payloads, the JWT specification includes a lesser-known sibling: JSON Web Encryption (JWE). JWE tokens are actually encrypted, providing confidentiality for payload contents. Unlike standard JWTs (technically called JWS—JSON Web Signature), JWE tokens cannot be decoded without the encryption key.

A JWE token has five parts instead of three, including headers for encryption algorithm and key management, an encrypted key, initialization vector, ciphertext, and authentication tag. The payload is genuinely encrypted using algorithms like AES-GCM, making it unreadable to anyone without the decryption key.

However, JWE introduces significant complexity. Key management becomes critical—losing encryption keys means losing access to all tokens encrypted with those keys. Performance overhead increases due to encryption and decryption operations. Implementation errors in cryptographic code can create vulnerabilities worse than storing data in plain sight.

Most applications don’t need JWE. The better approach is typically to keep sensitive data out of tokens entirely, using server-side storage with token-referenced identifiers. JWE should be reserved for specific scenarios where encrypted tokens provide clear advantages, such as sharing authentication across organizations with different trust boundaries.

Best Practices for JWT Security

Implementing secure JWT-based authentication requires following established best practices that address common vulnerabilities:

Never store sensitive data in JWT payloads. This bears repeating because it’s the most critical rule. Treat every JWT as if it will be read by an attacker, because statistically, many will be.

Use short expiration times. Keep JWT lifetimes measured in minutes or hours, not days or weeks. Shorter expiration windows limit the damage from compromised tokens.

Implement token refresh mechanisms. Issue short-lived access tokens alongside longer-lived refresh tokens stored securely. This limits exposure while maintaining user convenience.

Validate all JWT claims. Always verify the signature, check expiration, validate the issuer, and confirm the audience matches your application. Never trust an unverified JWT.

Use strong signing algorithms. Prefer RS256 (RSA signatures) over HS256 (HMAC) for better key separation. Avoid algorithm confusion attacks by enforcing expected algorithms server-side, never allowing the JWT header to dictate which algorithm to use.

Store signing secrets securely. Never hardcode secrets in application code or commit them to version control. Use environment variables, key management services, or dedicated secret storage solutions.

Implement proper HTTPS. Always transmit JWTs over encrypted connections. Network interception of unencrypted JWT traffic exposes tokens to attackers.

Consider token revocation strategies. While JWTs are stateless, critical applications should implement token blacklisting or maintain minimal session state for revocation capability.

Educating Development Teams

Perhaps the most important security measure is education. The JWT misconception persists because developers aren’t taught the distinction between encoding, signing, and encryption. Security training should explicitly address this gap, demonstrating how easily JWTs can be decoded and emphasizing the implications.

Code review processes should flag any JWT implementation storing sensitive data in payloads. Static analysis tools can be configured to detect common patterns of JWT misuse. Security testing should include checking JWT contents for exposed sensitive information.

Organizations should establish clear policies on JWT usage, specifying what data belongs in tokens and what requires server-side storage. Documentation should include examples of both correct and incorrect JWT implementations, making the security implications crystal clear.

Conclusion

The misconception that JWTs provide encryption has led to countless data leaks and continues to put systems at risk. Understanding that Base64 encoding is not encryption is fundamental to implementing secure authentication systems. Standard JWTs provide integrity verification through signatures, but they offer no confidentiality protection for payload contents.

For secure application development, treat JWT payloads as publicly readable data. Store only non-sensitive identifiers in tokens, maintain sensitive information server-side, and consider opaque tokens for session management in security-critical applications. When implementing JWTs, follow established best practices, validate all token claims, and use appropriate expiration times.

The security of modern web applications depends on developers understanding the tools they use. JWTs are powerful and useful when applied correctly, but dangerous when misunderstood. By recognizing that JWTs aren’t encrypted and designing systems accordingly, developers can leverage their benefits while avoiding the data leaks that give them such a bad reputation in security circles.

The next time you implement JWT-based authentication, remember: if you wouldn’t want that data in a server log file, it doesn’t belong in a JWT payload. Your users’ privacy and your application’s security depend on getting this right.

Related Topics

#JWT security, JSON Web Tokens, JWT encryption misconception, Base64 encoding vs encryption, JWT vulnerabilities, JWT data leaks, JWT best practices, JWT payload security, opaque tokens, session management security, JWT authentication, web application security, API security, JWT misuse, token-based authentication, JWT signing, JWE encryption, secure JWT implementation, JWT decoding, cybersecurity 2025, data breach prevention, JWT storage, sensitive data protection, JWT signature verification, HMAC SHA256, RS256 algorithm, token security, authentication tokens, JWT claims, stateless authentication, microservices security, OAuth security, JWT expiration, token revocation, developer security training, application security, PII protection, JWT vulnerabilities CVE, web token security, JWT decode example, secure coding practices, identity management, access control, JWT misconceptions, encryption best practices

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles