Opportunity

JWT kid header path traversal: how to stop key ID injection

The PainHunt Team · July 27, 2026 · 5 min read

TL;DR: A JWT's kid header is attacker-controlled text that many libraries hand straight to the filesystem. Put ../ in it and the verifier reads a file of the attacker's choosing as the signing key — which means they can then sign their own tokens. PainHunt's DevSecOps data carries a small but unusually intense cluster of these reports, alongside a matching temp-file race. The wedge is a library that enforces key ID whitelisting itself, plus static analysis that finds the pattern before it ships.

The evidence

PainHunt's DevSecOps category holds 171 high-scoring signals (10+/15), average intensity 7.6/10, average score 11.7, sourced mostly from GitHub (75), Mastodon (37), HackerNews (15), Reddit (11) and Dev.to (11).

Inside it, 10 signals form a key-handling cluster with intensity 7.9/10 — above the category average. The reports are specific, and all trace to the same shape:

  • JWT verification uses the kid (Key ID) header directly in a filesystem path, allowing path traversal.
  • The kid header accepts traversal characters like ../ because no input validation rejects them.
  • The consequence named repeatedly is secret key leakage leading to complete authentication bypass — not merely an unauthorized read.
  • No automated validation exists for cryptographic key identifiers in the affected authentication libraries.

The same cluster contains a structurally identical filesystem bug in shell and system-level scripts: time-of-check to time-of-use (TOCTOU) races in temporary file handling, exploitable through symlinks for arbitrary file writes, caused by missing atomic creation (mkstemp, O_EXCL) and unenforced 0600 permissions with no owner verification.

The reporters are open-source maintainers, security researchers and bug bounty hunters — the sources are dominated by GitHub, which is where this class of finding gets filed rather than discussed.

The fixes named in the data are equally specific: a hardened JWT library with built-in whitelist enforcement for key IDs, automated static analysis for kid header injection risks, a JWT security auditing tool, and a secure temporary file management library.

Why now

Two things made this cluster bigger than it should be.

First, kid is one of the few JWT header fields with no prescribed semantics. RFC 7515 says it's a hint for which key to use and stops there. Every library invents its own resolution, and "look up a file named after it" is the shortest path from spec to working code. The spec never said the hint is untrusted input, so implementations keep re-learning it.

Second, key rotation went mainstream. Multi-tenant services and short-lived signing keys made dynamic key lookup normal, so far more code paths now resolve a kid at verification time than did five years ago. The bug is old; the surface area is new.

Meanwhile the failure is unusually quiet. A traversal that leaks a signing key produces no failed logins and no anomalous errors — the attacker's subsequent tokens verify correctly. There is nothing for a WAF or an auth log to catch.

The wedge

Don't sell a scanner. Sell the safe default, then the proof it's in place.

  • Whitelist inside the library, not in the caller's hands. The verifier accepts a set of permitted key IDs, or a resolver function, and refuses anything else. Any kid containing a path separator or .. is rejected before it reaches a filesystem call, regardless of how the application configured it. This is the fix the reports ask for, and it belongs one layer below where teams keep getting it wrong.
  • Static analysis for the exact taint path. A rule that flags kid (and equivalent header-derived identifiers) flowing into open, readFile, require, or path joins. Narrow, high-signal, and shippable as a CI check or a linter plugin rather than another dashboard.
  • The same rule for temp files. TOCTOU on mkstemp-less temporary file creation is the second half of this cluster and detectable by the same class of analysis: non-atomic creation, missing O_EXCL, permissions wider than 0600, no owner check.
  • An audit mode that reports what is enforced. Maintainers filing these reports need to show a project that the fix landed. A verifiable statement — which key IDs are permitted, which paths are reachable — is more useful to them than a severity score.

Risks and honest caveats

  • The cluster is 10 signals, not 100. Intensity is high (7.9/10) and the reports are precise, but this is a narrow finding inside a 171-signal category. Treat it as evidence that the pattern is live and being filed, not as sizing for a market.
  • The obvious buyer may not pay. These reports come from open-source maintainers and bounty hunters. The people who need the hardened library are often the ones least able to buy tooling — the paying customer is more likely a platform team with an auth service they can't fully audit.
  • Established JWT libraries are free and entrenched. Replacing one is a security-sensitive migration, which is exactly the kind teams defer. A hardening wrapper or a strict mode contributed upstream may reach further than a new library, at the cost of a weaker business.
  • Static analysis lives or dies on false positives. A rule loose enough to catch indirect flows will fire on safe code; one tight enough to stay quiet will miss real bugs. This trade-off is the actual product, and it isn't solved by having the idea.

How to validate this further

Read the underlying DevSecOps signals in the Pain Point Browser, and check whether the maintainers filing them would pay or only adopt. Related security wedges in this data: MCP server SSRF and DNS rebinding protection and self-hosted CVE exposure response. To pressure-test the buyer question before writing code, run it through the Idea Validator and the checklist in how to validate a startup idea.

Frequently asked questions

What is JWT kid header path traversal?

The kid (key ID) header of a JWT names which key should verify the signature. Libraries that resolve that name straight to a file path let an attacker put ../ sequences in it, so a crafted token makes the verifier read an attacker-chosen file as the signing key. Reports in PainHunt's DevSecOps data describe this leading to key leakage and complete authentication bypass.

Why is a leaked signing key worse than a normal file read?

Because the same token that triggers the read is the one being verified. Once the attacker knows which bytes the verifier will treat as the key, they can sign tokens themselves. It converts a read primitive into unrestricted identity — the signature check still passes, so downstream authorization sees a legitimate user.

What would fix it at the library level?

The fixes named in the same reports are a whitelist of permitted key IDs enforced inside the library rather than left to callers, rejection of any kid containing traversal characters, and automated static analysis that flags kid values reaching filesystem calls.

Validate your idea against real demand

PainHunt scores hundreds of thousands of real user complaints by commercial potential — so you build what people already want.

Open the Pain Point Browser

Keep reading

JWT kid header path traversal: how to stop key ID injection | PainHunt