defensiva
JWT
JSON Web Token
authentication

JWT Security: Vulnerabilities, Algorithms and Best Practices

What a JWT really is, the most exploited bugs in pentesting (alg=none, kid injection, weak secret) and the defensive practices any audit expects.

SecraMay 8, 202611 min read

A JWT (JSON Web Token) is a compact, signed bearer token format that carries authenticated information between two systems, typically a server and a web or mobile client. The most common use is authentication: the server issues a JWT when the user logs in, the client stores it and sends it back on every subsequent request, and the server verifies the signature to confirm the token is still valid without keeping an active database session. It is standardised in RFC 7519 and underpins OAuth 2.0, OpenID Connect and most modern API architectures.

This guide covers what a JWT is, the internal structure, the common signing algorithms, the bugs most often exploited in penetration testing (alg=none, kid injection, weak secret, JWT confusion), how it compares with traditional sessions and the defensive practices any serious audit expects to see in place.

What a JWT is and what it is used for

A JWT is an ASCII text string that encodes structured information (claims) and signs it with a secret or a private key. The receiver verifies the signature and, if it is valid, trusts the claims.

Typical use cases:

  • Authentication. After login, the server issues a JWT that the client sends in the Authorization: Bearer <token> header on every subsequent request.
  • Authorisation. The JWT carries the user roles or permissions; the server decides what the user can do without going back to the database.
  • Service-to-service exchange. In microservice architectures, one service signs a JWT to authenticate against another.
  • Single Sign-On (SSO). The identity provider issues a JWT that works across all federated services (OpenID Connect). In corporate legacy environments, the XML-based alternative is SAML. Modern AitM phishing kits target exactly the SSO flow to capture tokens.

What makes JWT useful is not the cryptography itself (it is standard), but the compact self-contained format: it fits in a cookie or an HTTP header, transmits efficiently and allows stateless verification on the server.

Structure of a JWT: header, payload, signature

A JWT is three blocks separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0Iiwicm9sZSI6ImFkbWluIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Each block is JSON encoded in Base64URL.

Defines the signing algorithm and the token type. Decoded example:

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "key-2026-01"
}

Typical fields: alg (algorithm, mandatory), typ (type, optional), kid (key ID, identifies the key used when there is rotation).

Payload

Contains the claims (statements about the user or the session). There are registered claims (sub, iss, aud, exp, iat, nbf, jti) and custom application claims.

{
  "sub": "1234",
  "role": "admin",
  "exp": 1747900800,
  "iat": 1747897200
}

Important: the payload is not encrypted, only encoded. Anyone holding the token can read its contents. Do not put secrets, passwords or sensitive data that should not be visible to the client.

Signature

The cryptographic signature of the concatenated header and payload. It is computed with:

HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)

for symmetric algorithms (HS256), or with an RSA/ECDSA private key for asymmetric algorithms (RS256, ES256).

The signature is what guarantees that neither header nor payload have been modified after issuance. Without a valid signature, the server rejects the token.

Common signing algorithms

The algorithms most used in production and their operational differences:

AlgorithmTypeSignature sizeTypical use
HS256Symmetric (HMAC + SHA-256)256 bitsMonolithic apps, same service signs and verifies
HS384 / HS512Symmetric, longer hash384/512 bitsSame as HS256, larger security margin
RS256Asymmetric (RSA + SHA-256)2048-bit keyMicroservices, SSO, public APIs
RS384 / RS512Asymmetric, longer hash2048+ bit keySame as RS256, extra security
ES256Asymmetric (ECDSA P-256 + SHA-256)64 bytes signatureCompact tokens, same security level as RS256
ES384 / ES512ECDSA with larger curves96/132 bytesSame as ES256 with margin
EdDSAEd25519 curve64 bytesModern, recommended where supported
noneNo signature0Never use in production (known attack vector)

Quick decision: HS256 if the same service signs and verifies; RS256 or ES256 if there is a separation between issuer and verifier.

Common JWT bugs that appear in penetration testing

This is the category that most often shows up in penetration test reports on APIs and web applications:

1. alg: none

The RFC allows the none algorithm for unsigned tokens. Poorly configured libraries accept tokens with alg: none in the header and validate them without checking the signature. The attacker drops the signature and modifies the payload freely:

{"alg": "none", "typ": "JWT"}.{"sub": "victim", "role": "admin"}.

Defense: explicitly reject alg: none in the validation code. Configure a whitelist of accepted algorithms.

2. Asymmetric algorithm treated as symmetric

If the server is configured to validate RS256 but the library uses the public key as the HS256 secret, an attacker can sign tokens with the public key (which is accessible) and the server accepts them as valid HMAC. Historical vulnerability across many libraries; still shows up in legacy code.

Defense: validate that the header algorithm matches the algorithm expected for that key.

3. Weak secret in HS256

If the HMAC secret is short or predictable (a memorable phrase, "secret", password123), an attacker captures a token, extracts the signature and cracks it offline with hashcat or john the ripper in minutes to hours. Once the secret is cracked, the attacker can sign any token.

Defense: secret of at least 32 random bytes (64 recommended). Generate with openssl rand -base64 64.

4. Injection in kid

The kid field (key ID) selects the specific key the server uses to verify. If the code queries kid against a database without sanitisation, it opens SQL injection. If it reads a local file (/keys/{kid}.pem), it opens path traversal:

"kid": "../../../../dev/null"

Some exploits make the server validate the signature against a known system file, letting the attacker predict the signature.

Defense: validate kid against a whitelist, never use it directly in queries or paths.

5. JWT confusion across domains

If iss (issuer) or aud (audience) are not validated, a token issued for one application can be reused in another from the same provider.

Defense: always validate iss and aud against the value expected by the specific application.

6. Tokens without expiration or eternal expiration

Without exp or with exp very far away (years), a stolen token remains valid forever. Combined with insecure localStorage storage, a single XSS exfiltrates the token and the attacker keeps indefinite access.

Defense: short exp (15-60 minutes for access tokens), refresh tokens with a longer life managed separately, revocation handled via blacklist or key rotation.

7. HS256 signature with the word "secret" as the key

This still shows up in production code more often than it should. Defense: systematic configuration review before going to production, plus a SAST scanner with a specific rule.

The two approaches have real tradeoffs:

FeatureJWTTraditional session
Server storageStatelessState in DB/cache
Horizontal scalingTrivial (any instance validates)Requires shared session store
RevocationDifficult (valid until exp)Immediate (delete the DB entry)
Size0.5-2 KB per requestSmall cookie (32-128 bytes)
Client riskToken contains info, requires protectionOpaque cookie, less info exposed
RenewalRefresh token + rotationImplicit renewal

A reasonable decision:

  • JWT: public APIs, microservices, SSO, mobile apps. The defensive effort is worth it.
  • Traditional session: monolithic web applications with a single server or a few instances behind a load balancer. Simpler, fewer attack vectors.

JWT security best practices

The ones any audit expects to find in place:

  1. Algorithm whitelist. Strict validation of alg. Reject none. Do not mix symmetric and asymmetric.
  2. Strong secret. At least 32 random bytes for HS256, managed in a KMS or secret manager (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault).
  3. Key rotation. kid versions the key; the server keeps a set of active keys plus recent history. Rotate at least every 90 days.
  4. Validate iss, aud, exp, iat, nbf. Each one blocks a vector. It is not optional.
  5. Short exp. 15-60 minutes for access tokens. Refresh token managed separately.
  6. Client storage. HttpOnly cookies with SameSite=Strict beat localStorage (which is reachable from XSS). If localStorage is used, assume an XSS steals the token.
  7. No secrets or sensitive data in the payload. It is readable by anyone holding the token.
  8. Operational revocation. Token blacklist, immediate key rotation in case of compromise, logout that invalidates refresh tokens.
  9. Logs without tokens. Do not log the full JWT. If debugging is needed, log only sub and jti.
  10. Periodic external audit. Penetration testing that explicitly covers JWT against OWASP API Security Top 10 category 2 (Broken Authentication).

JWT and compliance

  • PCI DSS v4.0 (req. 8 and 6.4.6). Token management is part of the strong authentication required on the CDE. Any significant change requires technical testing.
  • GDPR (article 32). Appropriate technical measures. Tokens without expiration or revocation make the right to erasure harder to enforce.
  • NIS2 (article 21). Effectiveness of technical measures in authentication.
  • eIDAS 2 (future EU Digital Identity Wallet). Heavy use of signed claims; JWT and Verifiable Credentials formats overlap.

Frequently asked questions

Is JWT secure on its own?

Yes, when implemented correctly. The standard is solid (RFC 7519). Problems almost always come from implementation: alg: none allowed, weak secret, aud/iss/exp not validated, or insecure client storage. Poorly implemented JWT is one of the most frequent bug categories in API pentests.

What is the difference between JWT and OAuth 2.0?

OAuth 2.0 is an authorisation protocol (how a token is obtained); JWT is a token format (what it looks like). OAuth 2.0 can use JWT as the format or use opaque tokens. OpenID Connect (the identity layer on top of OAuth 2.0) does require JWT for id_token.

Should I encrypt the payload (JWE) instead of just signing it (JWS)?

JWS (signature) is enough by default. JWE (encryption) only makes sense if the payload contains sensitive information that should not reach the client, which is usually a sign the data should not be in the token in the first place. Practical rule: keep identifiers and permissions in the payload, sensitive data outside the token. The signature is then sufficient.

How long should a JWT live?

For access tokens: 15-60 minutes as a starting point. Longer tokens amplify the damage of theft. For refresh tokens: days or weeks, stored in HttpOnly cookies or in a backend with rotation on use.

How do you revoke a JWT before its expiration?

Three approaches: (1) blacklist in cache (Redis, Memcached) with revoked jti until exp; (2) per-user versioning (a counter in the database compared against a token claim); (3) signing key rotation when there is a massive compromise. Granular revocation is the main reason some teams prefer traditional sessions over JWT.

HttpOnly + Secure + SameSite=Strict cookie for traditional web applications. localStorage only when architecture demands it (some mobile SDKs), accepting that an XSS exfiltrates the token. Never sessionStorage for persistent tokens.

JWT at Secra

At Secra we audit JWT implementations as a routine part of our web, mobile and API penetration testing. We cover algorithm analysis, secret management, claim validation, client storage, rotation and revocation. If your organisation needs to audit the JWT implementation in a critical application before a release or a PCI/NIS2 certification, get in touch through contact or check the web and mobile application audit service.

About the author

Secra Solutions team

Ethical hackers with OSCP, OSEP, OSWE, CRTO, CRTL and CARTE certifications, 7+ years of experience in offensive cybersecurity, and authors of CVE-2025-40652 and CVE-2023-3512.

Share article