What JWT is and how it works

JWT (JSON Web Token) is an open standard defined by RFC 7519 for representing claims in a compact, portable and verifiable way. It is the foundation of stateless authentication in REST APIs, identity federation and authorization across microservices.

A JWT is composed of three parts separated by dots (.), each encoded in Base64URL:

  1. Header — the token type (JWT) and signing algorithm (alg), e.g. {"alg":"RS256","typ":"JWT"}
  2. Payload — a set of claims (assertions), such as sub (subject), exp (expiration), iss (issuer), aud (audience) and custom claims
  3. Signature — a digital signature generated over Header + Payload using the private key (RS256/ES256) or shared secret (HS256)

The receiver verifies the signature with the corresponding public key (or shared secret). If valid, the payload contents can be trusted. Important: the payload is not encrypted — only signed. Anyone with access to the token can decode and read its contents.

Legitimate uses of JWT

JWT is suitable for:

  • Stateless authentication: the server does not need to store session state — the user's identity is in the token, verifiable with the public key
  • Authorization across microservices: a centralized authentication service issues tokens that other services validate locally without querying a central database
  • Federation and SSO: standards such as OpenID Connect (OIDC) use JWT as an ID Token to prove the identity of the authenticated user
  • APIs consumed by multiple clients: mobile, SPA, CLIs and B2B integrations can share the same authentication mechanism

Classic vulnerabilities and how they work

Most attacks against JWT do not exploit raw cryptographic flaws — they exploit incorrect validation implementations. The OWASP API Security Top 10 lists authentication flaws as a critical API risk. Below are the most common vulnerabilities documented in the security literature.

1. The alg:none attack

RFC 7519 defines none as a valid algorithm for unsigned tokens. Libraries that accept the alg field directly from the token header without verification let an attacker switch the algorithm to none, strip the signature and present a forged token with any payload. The fix requires the server to specify the accepted algorithm regardless of what is in the header.

2. Algorithm confusion (RS256 to HS256)

This is the most sophisticated and often underestimated attack. When a server accepts both RS256 and HS256, an attacker can obtain the RSA public key (frequently exposed at /.well-known/jwks.json) and use it as the HMAC secret to sign a token with HS256. The server, on verification, uses that same public key as the secret — and the signature is valid. The attack has been documented and demonstrated by PortSwigger across multiple CVEs in popular JWT libraries.

3. Weak or predictable HMAC secret

When HS256 is used, security depends entirely on the entropy of the secret. Secrets such as secret, password or development strings left in production are trivially crackable with tools like hashcat or jwt_tool. Research shows that a significant fraction of production implementations use insecure secrets.

4. Missing claim validation

JWT libraries generally verify only the signature by default. The exp (expiration), nbf (not before), iss (issuer) and aud (audience) claims must be validated explicitly. An expired token accepted by the server, or a token issued for another service accepted without checking aud, is an authorization vulnerability — documented in the OWASP Cheat Sheet Series for JWT.

5. Storage in localStorage and XSS

Tokens stored in localStorage or sessionStorage are accessible to any JavaScript running on the page. A single XSS vector — whether in a text field, a URL parameter or a compromised npm dependency — enables full token exfiltration. This turns a low-impact vulnerability (reflected XSS) into a high-impact one (persistent session hijacking).

6. No revocation mechanism

By stateless design, a valid JWT cannot be invalidated before expiration without additional infrastructure. If a token is compromised — through a leak, XSS or device theft — the attacker keeps access until expiration. For tokens with a long exp (24h, 7 days or no expiration), the impact is severe.

7. Sensitive data in the payload

The payload is Base64URL-encoded, not encrypted. Passwords, national ID numbers, card numbers or health data present in the payload are exposed to any intermediary or anyone with access to the token — including system logs that record Authorization headers.

Vulnerabilities and mitigations table

Vulnerability Impact Mitigation
alg:none accepted by the server Full authentication bypass Pin the algorithm on the server; never trust the token's alg field
RS256/HS256 confusion Token forgery using the public key Accept only one algorithm per endpoint; separate keys by purpose
Weak HMAC secret Offline brute-force of the token Minimum 256 bits generated by a CSPRNG; prefer asymmetric algorithms
No exp/aud/iss validation Expired tokens or tokens from other services accepted Validate all claims explicitly in the library configuration
Token in localStorage Exfiltration via XSS HttpOnly cookie + SameSite=Strict + a strict CSP
No revocation Compromised token valid until it expires Short exp (5-15 min) + refresh token with rotation and a JTI denylist
Sensitive data in the payload Exposure of PII/financial data Identifiers only in the payload; sensitive data requires JWE (RFC 7516)
No key rotation Permanent compromise of the private key Scheduled rotation (e.g. 90 days); JWKS supporting multiple active keys

Implementation best practices

Algorithm and keys

Use asymmetric algorithms: RS256 (RSA-PKCS1-v1.5 + SHA-256), ES256 (ECDSA P-256) or EdDSA with Ed25519. Asymmetric algorithms eliminate the need to share secrets between services — each service can verify tokens with the public key without access to the issuing private key. Never use HS256 in architectures where multiple services need to verify tokens, since it would require distributing the secret to all of them, widening the attack surface.

Store private keys in secrets vaults with auditable access control. Rotate keys periodically and keep the JWKS endpoint updated with overlapping validity so that tokens in circulation are not invalidated.

Mandatory claims

Configure your library to explicitly validate: exp (with a maximum clock-skew tolerance of 60 seconds), iss (the expected issuer, fixed on the server), aud (the audience of the validating service) and jti (a unique UUID per token, for the denylist). Role claims (roles, scope) must be validated at the authorization layer, not just at authentication.

Token lifetime and rotation

Access tokens should expire in 5 to 15 minutes. Refresh tokens, stored hashed in the database, can last days or weeks — but with mandatory rotation on each use. When you detect reuse of an already-consumed refresh token, immediately invalidate the user's entire token family: this indicates a compromised refresh token and should raise a security alert.

Secure client-side storage

Tokens should be transmitted and stored in cookies with the HttpOnly (inaccessible to JavaScript), Secure (HTTPS only) and SameSite=Strict or Lax (CSRF protection) attributes. In SPAs that need to read the token, one alternative is the BFF pattern (Backend for Frontend), where the server manages cookies and exposes only a session verification endpoint.

JWT versus traditional session

The choice between stateless JWT and server-side session is an architectural decision with meaningful trade-offs, especially in regulated contexts such as fintechs and healthtechs.

A traditional session stores state on the server (database or Redis). The client keeps only an opaque identifier. Advantages: immediate revocation, no risk of data on the client, centralized control. Drawback: it requires shared state across server instances (sticky sessions or a distributed store).

Stateless JWT carries state in the token. Advantages: horizontal scalability without shared state, suitable for microservices and public APIs. Drawback: revocation is complex, compromised tokens are valid until they expire, and the attack surface includes the validation logic in each service.

For applications subject to Open Finance Brasil (BCB Resolution 32/2020) or PCI DSS, the ability to immediately revoke a session is often a requirement — which favors server-side sessions or JWT with an active denylist and a very short exp.

JWT in fintech APIs and regulated environments

In Brazilian fintechs regulated by the Central Bank, Open Finance Brasil adopts the FAPI 1.0 Advanced standard, which requires: access tokens with a maximum exp of 15 minutes, asymmetric algorithms (PS256 or ES256), mandatory aud validation and the use of PKCE in the authorization flow. The absence of any of these controls constitutes auditable non-compliance.

In payment APIs (PIX, TED, boleto), tokens with an inadequate scope or without aud validation can be reused across endpoints — allowing a token issued for a balance query to be used to initiate a transfer, if the server does not check the scope with sufficient granularity.

Penetration tests specialized in financial APIs must always cover: token replay, algorithm confusion, claim validation bypass and token leakage scenarios via logs and headers.

Tools and references

Frequently asked questions

What is JWT and what is it for?

JWT (JSON Web Token) is a compact, self-contained format for transmitting information between parties as a digitally signed JSON object. It is defined by RFC 7519 and widely used for stateless authentication in REST APIs, microservice authorization and the secure exchange of claims between systems. A valid JWT guarantees that the payload has not been tampered with after issuance — provided the signature is verified correctly.

What is the most critical vulnerability in JWT implementations?

The algorithm confusion attack is considered the most critical. It occurs when the server accepts tokens signed with HS256 using the RS256 public key as the secret. An attacker who obtains the public key (often exposed at /.well-known/jwks.json) can forge valid tokens. The mitigation requires the server to explicitly specify the accepted algorithm, without trusting the token header's alg field.

Is it safe to store JWT in localStorage?

No. localStorage is accessible to any JavaScript running on the page, making the token vulnerable to XSS attacks. Any injected script — including from malicious browser extensions or compromised dependencies — can exfiltrate the token. OWASP's recommendation is to store access tokens in HttpOnly and SameSite=Strict or Lax cookies, inaccessible to JavaScript and with native CSRF protection.

How does JWT revocation work if the token is stateless?

By design, stateless JWT has no native revocation mechanism — the token is valid until it expires. To revoke before expiration, you must maintain a denylist on the server with the invalidated JTIs (JWT IDs), or adopt short-lived access tokens (5-15 min) combined with long-lived refresh tokens stored with version control in the database. The token rotation pattern with invalidation of the previous refresh is the most widely adopted in production.

Can sensitive data be placed in the JWT payload?

No. A JWT payload is only Base64URL-encoded — not encrypted. Anyone with access to the token can decode it without knowing the key. This means a national ID number, password, card number or any sensitive data must never appear in the payload. To transmit data that needs to be encrypted, the correct standard is JWE (JSON Web Encryption), defined in RFC 7516.

JWT or traditional session: which to use in a fintech?

It depends on the threat model. Traditional sessions (a server-side session ID in a cookie) make immediate revocation easy and do not expose data on the client, making them preferable when granular session control is a priority. JWT is advantageous in microservice architectures and APIs consumed by multiple clients, where avoiding a database query on every request improves scalability. Fintechs with regulatory requirements (Open Finance, PCI DSS) tend to combine both: short-lived JWT for the API and a server-side session for the web portal.

API pentest and AppSec with Decripte

Decripte performs penetration tests specialized in APIs and web applications for companies of every size — from the sole proprietor to enterprises with more than 100,000 employees. Our team covers the main JWT attack vectors (algorithm confusion, validation bypass, token leakage, replay) within a rigorous technical scope and with delivery of actionable evidence.

Access the free Threat Management plan for an initial assessment of your security posture, or explore our AppSec and API pentest plans for continuous coverage.