Zion Boggan zionboggan.com ↗
82 lines · markdown
History for this file →
1
# Schism
2
 
3
Differential JWT verification harness. Feeds the same `(token, key, alg-allowlist)` triple into N JWT libraries and surfaces any verifier disagreement. Disagreements at the verification boundary are auth-bypass primitives.
4
 
5
## Premise
6
 
7
Auth bypass primitives in JWT libraries propagate to every program that uses the library. One finding maps to many bounty programs. The bug class is well known (alg confusion, kid injection, jku spoof, JWS critical header, JWE/JWS confusion, ECDSA r/s edge cases, padding, JSON-parser quirks at the header layer) but the libraries diverge in subtle ways that aren't covered by any single test suite. Wycheproof has static vectors; nobody runs live differential fuzzing across the modern JWT ecosystem.
8
 
9
## Targets (v1)
10
 
11
| ID | Lib | Lang | Why |
12
|----|-----|------|-----|
13
| `nodejwt` | `jsonwebtoken` (Auth0) | Node | Most-used npm JWT lib. ~10M weekly DLs. |
14
| `pyjwt` | `PyJWT` | Py | Most-used Python lib. Historical alg-confusion CVEs. |
15
| `pyjose` | `python-jose` | Py | Different impl, looser parsing. CVE-2024-33663 territory. |
16
| `panva` | `jose` (Panva) | Node | Most spec-compliant JS lib. Useful as oracle. |
17
| `gojwt` | `golang-jwt/jwt` v5 | Go | Most-used Go lib. Used in K8s, Helm, etc. |
18
 
19
v2: `nimbus-jose-jwt` (Java), `jsonwebtoken` (Rust), `lcobucci/jwt` (PHP).
20
 
21
## Architecture
22
 
23
```
24
              ┌────────────────────────────────┐
25
              │       orchestrator/differ.py    │
26
              │  (corpus → fanout → compare)    │
27
              └─────────────┬──────────────────┘
28
                            │ HTTP /verify
29
        ┌───────────┬───────┼────────┬───────────┐
30
        │           │       │        │           │
31
   ┌────▼───┐  ┌────▼───┐ ┌─▼─────┐ ┌▼──────┐ ┌─▼─────┐
32
   │nodejwt │  │ pyjwt  │ │pyjose │ │ panva │ │ gojwt │
33
   │:7001   │  │ :7002  │ │ :7003 │ │ :7004 │ │ :7005 │
34
   └────────┘  └────────┘ └───────┘ └───────┘ └───────┘
35
   each is a thin HTTP wrapper over the lib's verify() call,
36
   running in its own container, isolated, reproducible
37
```
38
 
39
Each target container exposes a single endpoint:
40
 
41
```
42
POST /verify
43
{
44
  "token": "<compact JWT>",
45
  "key":   "<PEM | symmetric | JWK>",
46
  "algs":  ["RS256", "HS256"]    // allowlist, mimics real-world API
47
}
48
→ { "valid": bool, "claims": {...} | null, "error": "<class>" | null }
49
```
50
 
51
The orchestrator submits each corpus case to all targets in parallel, computes the verdict tuple `(valid_per_target)`, and flags any tuple that isn't unanimous.
52
 
53
## Corpus
54
 
55
`corpus/seed.json` carries the v1 test cases. Each case has:
56
- `id` - short slug
57
- `class` - bug-class tag (`alg-confusion`, `none-alg`, `kid-injection`, `jku-spoof`, `crit-header`, `jws-jwe-confusion`, `ecdsa-r-s`, `header-json-quirk`, `dup-keys`, etc.)
58
- `token`, `key`, `algs` - the inputs
59
- `expected_unanimous` - what *should* happen if every lib agreed (`reject`)
60
- `notes` - pointer to RFC clause or prior CVE
61
 
62
Triage rule: any case where `valid` differs across targets is an auto-flag. Errors are bucketed by class so different error wording doesn't pollute results.
63
 
64
## Workflow
65
 
66
1. `docker compose up -d` - bring up all target wrappers
67
2. `python3 orchestrator/differ.py --corpus corpus/seed.json` - run the diff
68
3. `findings/<timestamp>.jsonl` - per-case verdict tuples; disagreements highlighted
69
4. Manual triage of disagreements → reproducer → CVE candidate
70
5. Bounty fanout: for each finding, enumerate downstream consumers via npm/PyPI/Go module dependency graph + Sourcegraph code search, prioritize by program payout
71
 
72
## Disclosure order
73
 
74
1. Upstream lib maintainer first (the bug source)
75
2. Then bounty programs whose scope includes the affected lib
76
3. Schism itself stays private until first finding wave is submitted
77
 
78
## Out of scope (explicitly)
79
 
80
- JWE encryption oracles (cover in v2)
81
- JWKS endpoint SSRF (different tool - covered by Flywheel's existing `test_ssrf_in_headers`)
82
- HTTP-level token transport (Authorization header parsing, cookie handling) - those are server-level, not library-level