JWT Security
JWT security depends on verifying the token's cryptographic signature and validating its claims before trusting any identity or authorization data. Secure implementations also constrain accepted algorithms, protect signing keys, and handle expiration and key rotation correctly.
A JWT normally contains a header, claims payload, and signature. The payload is encoded, not encrypted, so anyone who receives a token can decode its claims; confidentiality requires a separate mechanism such as encryption. For a signed JWT, the server must verify the signature using a trusted key before accepting security-sensitive claims.
Algorithm handling must be explicit. The verifier should allow only algorithms that the application intentionally supports and should use the appropriate key type for those algorithms. Never treat the token's `alg` field as sufficient authorization for choosing arbitrary verification behavior.
Claims need semantic validation after signature verification. Common checks include `exp` for expiration, `nbf` when present, `iss` for the expected issuer, `aud` for the intended audience, and application-specific identity or authorization claims. A valid signature alone does not mean that a token is appropriate for the current application or request.
Signing keys are security-critical credentials. They should be protected from unauthorized access, rotated according to operational requirements, and identified with a `kid` when multiple active verification keys exist. During rotation, applications commonly retain old public keys long enough to validate tokens issued before the rotation while new tokens use the new signing key.
For Java backends, prefer a mature JWT library and its standard verification APIs rather than implementing Base64 decoding, signature verification, or claim parsing yourself. Keep authorization decisions separate from token parsing: successfully authenticating a token does not automatically grant every permission represented by untrusted or insufficiently validated application data.
Key points
- Verify the signature before trusting security-sensitive JWT claims.
- Allowlist expected algorithms and use compatible, trusted keys.
- Validate `exp`, `nbf`, `iss`, and `aud` when applicable.
- Treat JWT payloads as readable data unless encryption is explicitly used.
- Protect, rotate, and correctly select signing keys, including `kid` handling.
Interview questions
What is the difference between validating a JWT signature and validating its claims?
Signature verification establishes that the token was produced by someone possessing the expected signing key and that its signed contents have not been altered. Claim validation determines whether that otherwise authentic token is acceptable in the current context. For example, a service can require the expected `iss` and `aud` and reject an expired `exp`. Both layers matter because an authentic token can still be expired or intended for another service.
What security checks should a Java backend perform when validating a JWT?
The backend should verify the cryptographic signature with a trusted key and an explicitly allowed algorithm. It should then validate relevant claims such as expiration, issuer, and audience. Application-specific authorization data should only be used after those checks succeed. The verifier should reject malformed, expired, incorrectly issued, or otherwise unsuitable tokens.
Why is trusting the JWT `alg` header dangerous?
The `alg` value is supplied by the token and therefore cannot by itself define the server's security policy. If verification logic accepts algorithms or key types that the application did not intend to support, an attacker may be able to exploit a mismatch in verification behavior. The application should configure an explicit algorithm allowlist and use the corresponding trusted key material. The JWT library should perform the cryptographic verification rather than custom application code.
How would you handle JWT signing-key rotation?
I would introduce a new signing key for newly issued tokens while retaining the previous verification key for tokens that are still within their accepted lifetime. Tokens can identify their key with `kid`, allowing the verifier to select the corresponding trusted public key from a controlled key set. The old key can be retired after the maximum relevant token lifetime and an operational safety window. Rotation and emergency key revocation should be designed together so compromised credentials can be invalidated promptly.
When would you choose JWTs over server-side sessions, and what trade-offs matter?
JWTs can be useful when independently verifiable claims and distributed services make centralized session lookup undesirable. They also make revocation and immediate state changes harder because a self-contained token may remain valid until expiration unless additional mechanisms are introduced. Server-side sessions make centralized invalidation and mutable session state straightforward but require shared or reachable session storage in a distributed deployment. The choice should therefore follow the application's consistency, revocation, scaling, and security requirements rather than assuming JWTs are inherently safer.