GO KALI FREE
IntermediateWeb Security

JWT Security: JSON Web Token Attacks and Best Practices

Learn about JSON Web Token security including common vulnerabilities like algorithm confusion, key leakage, and implementation flaws with defense strategies.

#JWT#JSON Web Token#Authentication#Token Security#Web Security

When a Token Becomes a Backdoor

A server issues a JWT that says {"user": "admin", "role": "user"}. The attacker decodes it (the payload is just base64, not encrypted), changes "role": "user" to "role": "admin", re-encodes it, and sends it back. If the server does not verify the signature properly — or uses the "none" algorithm — the attacker just escalated to admin. JWT security is about making sure tokens cannot be forged, tampered with, or replayed.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Prerequisites

Before studying JWT security, you should understand:

  • **Web Security Fundamentals** — Authentication vs authorization
  • **Session Security** — How sessions compare to tokens
  • **Cryptography Basics** — HMAC, RSA, digital signatures
  • **Node.js/Python** — For implementation examples
  • JWT Structure

    Header

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

    Payload

    {
      "sub": "user123",
      "name": "John Doe",
      "role": "admin",
      "iat": 1516239022,
      "exp": 1516242622,
      "iss": "https://auth.example.com",
      "aud": "https://api.example.com"
    }
    

    Signature

    HMACSHA256(
      base64UrlEncode(header) + "." + base64UrlEncode(payload),
      secret
    )
    

    Common JWT Attacks

    Algorithm Confusion Attack

    The most critical JWT vulnerability. When the server uses asymmetric algorithms (RS256) to verify tokens but the library accepts the algorithm from the token header, an attacker can change the algorithm to HS256 and sign with the public key:

    import jwt
    
    # Vulnerable — accepts algorithm from token
    def verify_token_vulnerable(token):
        payload = jwt.decode(token, options={"verify_exp": True})
        return payload
    

    Attack: Get the server's public key (often at /.well-known/jwks.json), create a token with alg: "HS256", sign using the public key as the HMAC secret.

    Weak or Exposed Secret

    # Crack JWT secret with hashcat
    hashcat -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt
    
    # Use jwt_tool for analysis
    python jwt_tool.py eyJhbGciOiJIUzI1NiJ9... -C -d /usr/share/wordlists/rockyou.txt
    

    None Algorithm Attack

    Setting the algorithm to "none" to bypass signature verification:

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

    JWK Injection

    The token contains an embedded public key (jwk header) that the server uses for verification. An attacker generates their own key pair, embeds the public key, and signs with their private key.

    Secure JWT Implementation

    Proper Algorithm Validation

    import jwt
    
    def verify_token_secure(token):
        try:
            payload = jwt.decode(
                token,
                public_key,
                algorithms=["RS256"],
                audience=["https://api.example.com"],
                issuer="https://auth.example.com",
                options={
                    "verify_exp": True,
                    "verify_iat": True,
                    "verify_aud": True
                }
            )
            return payload
        except jwt.InvalidTokenError as e:
            raise AuthenticationError(f"Invalid token: {e}")
    

    Use Asymmetric Algorithms

    # Generate RSA key pair
    openssl genrsa -out private.pem 2048
    openssl rsa -in private.pem -pubout -out public.pem
    

    Token Creation

    import jwt
    from datetime import datetime, timedelta
    
    def create_access_token(user_id, role):
        payload = {
            "sub": user_id,
            "role": role,
            "iat": datetime.utcnow(),
            "exp": datetime.utcnow() + timedelta(hours=1),
            "iss": "https://auth.example.com",
            "aud": "https://api.example.com",
            "jti": str(uuid.uuid4())
        }
        token = jwt.encode(payload, private_key, algorithm="RS256")
        return token
    

    Token Revocation

    Since JWTs are stateless, implement a denylist:

    import redis
    
    redis_client = redis.Redis()
    
    def revoke_token(jti, expires_in):
        redis_client.setex(f"revoked:{jti}", expires_in, "1")
    
    def is_token_revoked(jti):
        return redis_client.exists(f"revoked:{jti}")
    

    Real-World Examples

    Auth0 JWT Vulnerability (2020): A critical vulnerability in multiple JWT libraries allowed the none algorithm attack even when explicitly disabled.

    CVE-2022-23529 (jsonwebtoken): Remote code execution in the popular jsonwebtoken npm library allowed attackers to execute code through crafted JWTs.

    Microsoft Azure AD (2021): Tokens signed with a misconfigured Azure AD tenant could be used to access resources in other tenants.

    Common Mistakes

    Not validating the algorithm: Enables algorithm confusion attacks.

    Using weak secrets: Dictionary words for HMAC secrets can be cracked with hashcat.

    Not validating expiration: Tokens that never expire increase leak damage.

    No audience or issuer validation: Tokens from any issuer are accepted.

    Long token lifetimes: Access tokens valid for weeks increase the attack window.

    Best Practices

  • **Use asymmetric algorithms (RS256/ES256)**
  • **Explicitly validate the algorithm** — Never accept from token header
  • **Keep access tokens short-lived** — 15-60 minutes
  • **Validate all claims** — exp, iat, aud, iss
  • **Use unique token IDs (jti)** — Enable revocation
  • **Never store secrets in code** — Use environment variables
  • Related Tools

  • **jwt_tool** — Comprehensive JWT testing toolkit
  • **jwt.io** — Online JWT debugger
  • **Hashcat** — Crack weak JWT secrets (mode 16500)
  • **Burp Suite** — JWT extension for intercepting tokens
  • Related Articles

  • Authentication Attacks
  • Session Security
  • Web Security Fundamentals
  • Password Security Guide
  • Summary

    JWT has several critical security pitfalls. The most dangerous is the algorithm confusion attack. Defenses include explicit algorithm validation, short token lifetimes, proper claim verification, and using asymmetric signing algorithms.

    Knowledge Check

  • How does the algorithm confusion attack work against JWT?
  • Why is HS256 riskier than RS256 for distributed systems?
  • What claims should always be validated when verifying a JWT?
  • How does the "none" algorithm attack work?
  • What is the purpose of the jti claim?
  • Frequently Asked Questions

    What is a JWT and how is it structured?

    A JWT (JSON Web Token) is a compact, URL-safe token with three base64url-encoded parts separated by dots: header (algorithm and type), payload (claims like user ID and expiry), and signature (integrity verification). See the [Session Security](/learn/session-security) article for how JWTs compare to server-side sessions.

    What is the algorithm confusion attack?

    Algorithm confusion exploits servers that accept the algorithm from the token header. If the server uses RS256 (asymmetric) but the attacker changes the header to HS256 (symmetric), they can sign the token with the server's public key. Always explicitly specify allowed algorithms.

    How does the 'none' algorithm attack work?

    Some JWT libraries accept an algorithm of 'none', which means no signature verification. The attacker creates a token with alg:'none' and the server accepts it without validation. Always explicitly reject the 'none' algorithm.

    Why should asymmetric algorithms like RS256 be preferred?

    RS256 uses a private key to sign and a public key to verify. Even if the public key is exposed (e.g., at /.well-known/jwks.json), attackers cannot forge tokens without the private key. HS256 uses a shared secret that must remain secret on both sides.

    How do you crack a weak JWT secret?

    Use hashcat in mode 16500 or jwt_tool with a wordlist. Weak HMAC secrets (dictionary words, short strings) are crackable in seconds. Always use cryptographically strong, long secrets for HMAC-based JWT signing.

    What JWT claims should always be validated?

    Always validate exp (expiration), iat (issued at), aud (audience), and iss (issuer). Exp prevents indefinitely valid tokens, while audience and issuer validation prevents tokens intended for one service from being accepted by another.

    How do you revoke a JWT before it expires?

    Since JWTs are stateless, implement a denylist using Redis or a database. Store revoked token IDs (jti) with their expiry. The server checks the denylist on every request. This is essential for logout and token revocation scenarios.

    What is JWK injection and how does it work?

    JWK injection occurs when a server trusts an embedded public key in the token's jwk header. An attacker generates their own key pair, embeds the public key in the token, and signs with their private key. The server then verifies with the attacker's key.

    What is CVE-2022-23529?

    CVE-2022-23529 was a critical vulnerability in the jsonwebtoken npm library that allowed remote code execution through crafted JWTs. It highlighted the importance of keeping JWT libraries updated and using well-maintained implementations.

    How long should JWT access tokens be valid?

    Access tokens should be short-lived — typically 15-60 minutes. Use refresh tokens (stored securely) to obtain new access tokens. This limits the damage window if a token is compromised. Long-lived tokens increase exposure significantly.