A practical JWT pentest workflow
A field-tested sequence for testing JWT implementations, from collection and decoding to claims, keys, replay, and reporting.
A good JWT pentest is not a bag of tricks fired randomly at one token. It is a workflow. You collect tokens from real flows, classify issuers and audiences, understand where verification happens, then test specific failure modes with evidence. The goal is not to prove that JWTs are scary. The goal is to show where this implementation trusts the wrong thing.
Collection and inventory
Start by capturing tokens from login, refresh, password reset, invite links, mobile APIs, websocket upgrades, and background requests. Put them in a table with issuer, audience, algorithm, kid, lifetime, scopes, and where each token was accepted. Do not paste raw production tokens into shared notes. Store redacted headers and payloads, and keep secrets in the testing vault.
Decode before attacking. Many findings appear in plain claims: long lifetimes, missing aud, broad scopes, tenant identifiers, email verification flags, or debug fields that should not leave the issuer. Compare tokens from two users and two tenants. Differences often reveal the authorization model faster than source code.
Signature and key tests
Test algorithm pinning, alg: none, HS256 versus RS256 confusion, unknown kid, duplicate kid, and JWKS cache behavior. For each test, change one variable at a time. Keep exp, iss, and aud valid unless you are specifically testing them. If every tampered token returns the same early rejection, that is useful evidence. If different endpoints fail differently, map that split.
Claim abuse and authorization
Modify sub, tenant_id, org_id, role, scope, and feature flags. Try object access across tenants with a valid low privilege token. For GraphQL, test resolver-level authorization, not just the HTTP endpoint. For file downloads, check signed URL creation and actual file retrieval separately. Many systems authorize one and forget the other.
Replay and lifecycle
Check whether logout revokes anything server side. Reuse old refresh tokens after rotation. Test expired tokens just inside and outside clock skew. Try tokens after password change, MFA reset, role removal, and user disablement. Lifecycle bugs are often more exploitable than exotic crypto mistakes because they match real attacker timelines.
Reporting well
A strong report includes the exact token property changed, the endpoint, expected result, actual result, and impact. Avoid dumping tokens. Include safe reconstruction steps with redacted payloads and timestamps. The best remediation is usually specific: pin RS256 for issuer X, reject unknown kid without unbounded fetch, recheck tenant ownership on route Y.