Skip to content

JWT Claim Fuzzing

SeverityMedium
ClassificationsCWE-20: Improper Input Validation
Attack PatternCAPEC-28: Fuzzing
OWASP CategoryOWASP API8:2023 Security Misconfiguration

APIs that decode JWT claims into typed fields (structs, ORMs, template variables, SQL parameters, …) don’t always validate a claim’s type or length before using it. If a claim that’s normally a short string arrives as a number, an array, an oversized string, or null, the server may throw an unhandled exception, leak a stack trace, or respond very differently in size — all signs the claim was consumed without validation, and a starting point for further exploitation (denial of service, information disclosure, or worse if the value reaches a template engine, SQL query, or shell).

This is distinct from the other JWT vulnerabilities documented here: it isn’t a single crafted exploit but a fuzzing pass — every claim is mutated with a battery of edge-case values and each response is compared against a reference response to spot anomalies.

JWTop mutates each claim value in turn, keeping every other claim and the header untouched, and drops the signature (since it no longer matches the mutated payload):

Original claim: "sub": "user123"

Type confusion: "sub": ["user123"], "sub": 1337, "sub": {"a":"b"}, "sub": true

Oversized string: "sub": "AAAA... (10,000 characters by default)

Special characters: "sub": "' OR '1'='1", "sub": "<script>alert(1)</script>", "sub": "../../../../etc/passwd", "sub": "${7*7}{{7*7}}", …

Null value: "sub": null

Each mutated token is sent to the target and the response is compared against the reference response captured from the original, unmutated token. A mutation is flagged when:

  • the response status is 5xx
  • the response body matches a known stack-trace/unhandled-exception signature (e.g. Traceback (most recent call last), panic: ... goroutine, NullPointerException, Whitelabel Error Page, …)
  • the response body length differs from the reference by more than 50%

fuzz is off by default — enable it with --fuzz alongside the fixed check set:

Terminal window
jwtop crack [token] --url [url] --fuzz

Use --fuzz-max-string-len to change the oversized-string payload length (default 10000):

Terminal window
jwtop crack [token] --url [url] --fuzz --fuzz-max-string-len 100000
  • Denial of service: an unhandled exception on every request carrying a malformed claim can crash a worker process or exhaust resources on an oversized value.
  • Information disclosure: a leaked stack trace can reveal internal file paths, framework/library versions, database schema, or other details useful for further attacks.
  • Downstream injection: if a claim value reaches a SQL query, shell command, or template engine without sanitization, the special-character payloads used here are a starting point for SQL injection, command injection, or server-side template injection.
  1. Validate claim types and lengths after decoding, before using them — don’t assume a claim you expect to be a short string actually is one.

  2. Use a schema or typed struct to decode claims (many JWT libraries support this) so an unexpected type fails decoding cleanly instead of propagating into application logic.

  3. Return generic error responses for malformed input — never let a stack trace or exception message reach the client.

  4. Bound claim sizes at the API gateway or middleware layer (e.g. reject requests where the token or an individual claim exceeds a sane length) before they reach application code.

  5. Sanitize/parameterize any claim value used in a SQL query, shell command, template, or log line, the same as any other untrusted user input.