None of Your Business — Bypassing JWT Signature Validation
How a forgotten 'alg: none' code path turned a read-only audit token into full admin access.
Finding has been remediated and disclosed to the client. All screenshots below are recreated mockups with fabricated data — no real hosts, credentials, or personal information from any engagement.
The Setup
Every API pentest starts the same way: get a token, decode it, poke it. This time the target was a fairly standard REST backend sitting behind an API gateway — Bearer auth, JSON responses, the usual admin/audit role split. I’d been handed a low-privilege audit account: read-only access, no write scopes, nothing exciting on paper.
Naturally, the first stop wasn’t the API surface. It was the token itself.
Recon: Reading the Token
Dropped the JWT into a decoder and looked at the header:
{
"alg": "HS256",
"typ": "JWT"
}
Standard HMAC signing. Nothing screams “vulnerable” yet — but alg confusion bugs are common
enough in home-grown or older JWT middleware that it’s always worth a poke before moving on.
The payload had the shape you’d expect from a role-based system:
{
"roleId": 135,
"roleName": "AUDIT_USER",
"roleType": "READ_ONLY",
"status": "ACTIVE"
}
The Bug: Signature Exclusion via alg: none
Some JWT libraries — especially older ports or homegrown parsers — treat alg as advisory
rather than mandatory. If the server accepts none as a valid algorithm, it means: trust
this payload, don’t bother checking the signature.
I ran a quick exclusion matrix against the auth header — flipping the algorithm field through a handful of values and either stripping or mangling the signature segment on each request:

Every single variant came back 200 OK with a fully populated response. The signature
verification wasn’t just weak — for this endpoint, it wasn’t happening at all once alg left
the small set of values the developer had explicitly tested against.
From Read-Only to God Mode
Here’s where it stopped being a curiosity. Since the server wasn’t validating the signature, it also wasn’t validating that I had generated that token. I could hand-craft the payload directly:
{
"roleId": 1,
"roleName": "SYSTEM_ADMIN",
"roleType": "FULL_ACCESS",
"status": "ACTIVE"
}
Base64-encode header + payload, tack on an empty (or garbage) signature segment, send it as the Bearer token — and the gateway happily treated me as a fully privileged admin. No password, no MFA prompt, no second factor to bypass. Just an unsigned claim the server chose to believe.

From there it was a normal admin session: user enumeration, role management endpoints, the whole back office. The “audit” account I’d started with had effectively become root for the application layer.
Why This Keeps Happening
alg: none bugs feel like a relic — CVE-worthy write-ups about this exact class go back over
a decade — but they resurface constantly in:
- Custom JWT parsing logic instead of a maintained library
- Middleware upgrades that silently reintroduce permissive algorithm handling
- API gateways that terminate auth before the app, and assume downstream services will re-validate (they often don’t)
- Libraries where “algorithm allowlisting” is opt-in instead of the default
The root cause is almost always the same: the server trusts the client to declare how it should be verified. A JWT should never get to vote on its own validation method.
Lessons
For defenders:
- Hard-code the expected algorithm server-side. Never read
algfrom the incoming token to decide how to verify it. - Explicitly reject
noneat the library/config level — don’t rely on it being “off by default,” confirm it. - Treat JWT verification failures as loud events, not silent 200s.
- Re-validate tokens at every service boundary, not just the gateway.
For attackers/testers:
- Always run the algorithm exclusion matrix (
none, case variations,HS256→RS256confusion) on every JWT-authenticated endpoint, even ones that look boring. - A “read-only audit” token is still worth full attention — privilege escalation bugs love low-value accounts precisely because nobody hardens the ceiling above them.
- Confirm impact before declaring critical: forging a token is one thing, proving it maps to real unauthorized actions (data access, state change) is what makes the finding land.
