Detection Engineering

JWT Misconfiguration: Detection and Defense

JWT misconfiguration detection and defense — alg:none, RS256-to-HS256 confusion, and kid injection, with header-logging detection, Sigma rules, and MITRE mapping.

A hardware security key on a black workbench beside a screen showing a segmented token, one segment edged in red

JWT misconfiguration almost always reduces to one mistake: the verifier lets the token choose how it is checked. Three variants follow — alg:none acceptance, RS256-to-HS256 algorithm confusion, and kid injection. The fix is a single rule: the server pins the algorithm and key, and never reads them from the token. The detection is equally tight: log the JWT header on every auth event and alert on any algorithm your service does not issue.

JSON Web Tokens carry identity, so a forged token is an authentication bypass. This bug class has existed since Tim McLean’s 2015 disclosure of critical vulnerabilities in JWT libraries (CVE-2015-9235), yet new instances keep shipping.

What is a JWT misconfiguration?

A JWT misconfiguration is any verifier setup that lets an attacker influence how their own token is validated. Because a JSON Web Token is just identity the server trusts, a verifier that reads the algorithm or key from the attacker-supplied header can be tricked into accepting a forged token — an authentication bypass with no password, no MFA prompt, and no lockout.

This maps to MITRE ATT&CK T1550.001 — Use Alternate Authentication Material: Application Access Token and, for the resulting access, T1078 — Valid Accounts. The damage looks like legitimate use, which is exactly why header-level detection matters.

What are the JWT algorithm confusion attacks?

Every variant manipulates the token header to control verification, and each leaves a loggable fingerprint.

VariantWhat the attacker changesHeader fingerprintDefense
alg:noneSets alg to none, drops the signature"alg":"none" (any casing), empty 3rd segmentReject none unconditionally
RS256 → HS256Switches alg, signs with the public keyHS256 token at an RS256-only servicePin one algorithm family
kid injectionPoints key lookup at a file/query they controlTraversal/SQL chars in kidValidate/whitelist kid

In RS256→HS256 confusion specifically, the server verifies RSA tokens with its public key (published at /.well-known/jwks.json by design), and the attacker signs the forgery with that public key as the HMAC secret. A verifier that reads the algorithm from the token validates it.

How to detect JWT attacks from auth telemetry

Your service knows which algorithm and key IDs it legitimately issues. Anything else in an inbound token header is an attack or a bug. Log the decoded JWT header on every auth event, and the variants light up. This is the same auth-event logging that surfaces broken access control.

Detect unexpected algorithms

If your service issues only RS256, an inbound HS256 or none token is, by definition, not yours — the highest-fidelity JWT detection you can deploy.

Sigma Inbound JWT With Unexpected Algorithm
title: Inbound JWT With Unexpected Algorithm
id: 6c1f3a92-darkpwn-illustrative
status: experimental
logsource:
  product: application
  service: auth
detection:
  selection:
    jwt_header_alg:
      - 'none'
      - 'None'
      - 'NONE'
      - 'nOnE'
      - 'HS256'   # for a service that only issues RS256
  condition: selection
falsepositives:
  - Services that legitimately accept symmetric algorithms (tune per service)
level: high

Detect kid injection

A kid value containing path-traversal or SQL metacharacters is never legitimate — it means the attacker is probing the key-lookup mechanism.

Sigma JWT kid Header Containing Injection Characters
title: JWT kid Header Containing Injection Characters
id: 1e8d5b27-darkpwn-illustrative
status: experimental
logsource:
  product: application
  service: auth
detection:
  selection:
    jwt_header_kid|contains: ['../', '..\\', "'", ' or ', 'union select', '/dev/null']
  condition: selection
falsepositives:
  - Unusual but legitimate key-id naming schemes (audit before enforcing)
level: high

How to test your JWT detection

Validate the rules against your own staging service:

  1. Mint a normal token and confirm it passes and generates an issuance event.
  2. Submit an alg:none token (any casing) and confirm both rejection and an alert.
  3. Submit an HS256 token signed with the public key and confirm rejection.
  4. Submit a kid with ../ and confirm the injection rule fires.

Bake those four cases into CI so a future library-default change fails the build, not production.

How to prevent JWT misconfiguration

One control closes the whole class; the rest are depth.

  • Do not mix symmetric and asymmetric families on one verifier — that is the precondition for algorithm confusion.
  • Validate iss, aud, and exp on every token; missing issuer validation has enabled cross-realm token acceptance.
  • Keep JWT libraries patched and pinned — confusion bugs are fixed by upgrades, and the advisories are not always loudly announced.
  • Use short exp windows plus revocation for high-value sessions to shrink a forged token’s lifetime.

Common JWT security mistakes

  • Calling verify(token, key) without pinning the algorithm. The library infers it from the token — the root cause of the whole class.
  • Accepting both HS* and RS* on one path. The precondition for confusion.
  • Skipping claim validation. No iss/aud/exp check means stolen or cross-tenant tokens sail through.
  • Long-lived tokens with no revocation. A forged token is valid until it expires.

JWT security checklist

Pin this next to every verifier you own:

  1. Pin the expected algorithm explicitly at every verifier — never infer it from the token.
  2. Reject alg:none unconditionally, in any casing (lowercase before comparing).
  3. Never accept both symmetric and asymmetric families on one verification path.
  4. Validate iss, aud, and exp on every token.
  5. Log the decoded JWT header (alg, kid) on every authentication event.
  6. Alert on any algorithm you do not issue, and on kid values with traversal/injection chars.
  7. Use short exp windows plus revocation for high-value sessions.
  8. Keep JWT libraries patched and pinned; track their CVEs (e.g. CVE-2022-21449).
  9. Add a CI test asserting alg:none and HS256-signed-with-public-key tokens are both rejected.

The takeaway

JWT security is one decision repeated everywhere: the server chooses the algorithm and key, never the token. Pin it, reject none, log the header, and alert on any algorithm you do not issue. Round out your session-security coverage with SQL injection detection and the wider Detection Engineering pillar.

Training & tools referenced

Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.

  • TryHackMeAuthorized labs to practice JWT attack detection and hardeningSecurity Training
    Start training

Frequently asked questions

What is the alg:none JWT attack?

The attacker sets the token's header algorithm to none and removes the signature. A verifier that trusts the header skips signature checking and accepts the forged token. The fix is to reject none unconditionally, in any casing, and to pin the expected algorithm.

How does RS256 to HS256 confusion work?

The server signs with an RSA private key and verifies with the public key, which is published by design. The attacker switches the header to HS256 and signs a forged token using that public key as the HMAC secret. A verifier that reads the algorithm from the token validates the forgery. Pin one algorithm family per verifier to close it.

How do you detect a forged JWT?

Log the decoded JWT header on every auth event and alert on any algorithm your service does not issue, on none in any casing, and on kid values containing traversal or injection characters. Forged sessions also show up as authenticated actions with no matching token-issuance event.

How do you prevent JWT algorithm confusion?

Always pass the expected algorithm and key explicitly to the verifier, never let the library infer them from the token, and never configure one verifier to accept both symmetric and asymmetric algorithm families. Validate iss, aud, and exp on every token.

Newsletter

Liked this breakdown?

Defensive security research — detection, hardening, and hardware — delivered when there is something worth saying. No spam, unsubscribe anytime.