Security Resources

⌘K
  1. Home
  2. Security Resources
  3. Secure Code Explain
  4. JWT Authentication Bypass

JWT Authentication Bypass

Here is an example of Node.js code that is vulnerable to a JWT authentication bypass:

🥺 Vulnerable Code

const jwt = require("jsonwebtoken");

function getUser(req) {
  const token = req.headers.authorization.split(" ")[1];

  // Vulnerable: decode only reads the payload, it never checks the signature
  const claims = jwt.decode(token);

  return { id: claims.sub, role: claims.role };
}

// Almost as bad - no algorithm allowlist, so alg can be swapped:
// const claims = jwt.verify(token, publicKey);

jwt.decode() is base64 decoding with extra steps. An attacker mints {"sub":"1","role":"admin"}, sends it with any signature, and becomes an administrator. The commented alternative is the classic algorithm confusion bug: without an allowlist, a token signed with HS256 using the public RSA key as the HMAC secret verifies successfully, and alg: none is accepted by some libraries.

😎 Secure Code

Here is a version of the same code that is secured against a JWT authentication bypass:

const jwt = require("jsonwebtoken");

const VERIFY_OPTIONS = {
  algorithms: ["RS256"],                 // pinned, so alg in the header is ignored
  issuer: "https://auth.example.com/",
  audience: "api://reports",
  maxAge: "15m",
  clockTolerance: 5,
};

function getUser(req) {
  const header = req.headers.authorization || "";
  if (!header.startsWith("Bearer ")) {
    throw new AuthError("Missing bearer token");
  }

  const claims = jwt.verify(header.slice(7), publicKey, VERIFY_OPTIONS);

  if (revocationList.has(claims.jti)) {
    throw new AuthError("Token revoked");
  }

  return { id: claims.sub, role: claims.role };
}

The algorithm list is pinned in code, the issuer and audience are checked so a token from another service is useless here, and short lifetimes plus a jti revocation lookup limit how long a stolen token works. Keep signing keys in a KMS or secret manager, rotate them, and re-read the role from your own datastore for anything sensitive instead of trusting a claim that was minted minutes ago.