HIGH identification failuresjwt tokens

Identification Failures with Jwt Tokens

How Identification Failures Manifest in JWT Tokens

Identification failures in JWT‑based APIs occur when the server incorrectly trusts a token that it should reject. The most common manifestations are:

  • Missing or weak signature verification – the code accepts a token without checking its cryptographic signature, or uses a secret that is guessable or hard‑coded.
  • Accepting the "none" algorithm – some libraries allow the alg header to be set to none, which tells the verifier to skip signature checks.
  • Missing expiration (exp) or not‑before (nbf) validation – an old token can be replayed indefinitely.
  • Key ID (kid) injection – an attacker supplies a malicious kid value that points the verification routine to an attacker‑controlled key.
  • Weak secret or poor key management – using a low‑entropy HMAC secret or exposing RSA private keys in client‑side code.

These flaws map directly to the OWASP API Top 10 2023 category Broken Authentication (API2). For example, a Node.js endpoint that looks like the following is vulnerable:

const jwt = require('jsonwebtoken');
app.get('/api/data', (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];
  // VERIFICATION IS MISSING – the token is trusted blindly
  const payload = jwt.decode(token, { complete: true });
  if (!payload || payload.payload.sub !== 'expectedUser') {
    return res.status(403).send('Forbidden');
  }
  // …process request
});

Because jwt.decode only parses the token and does not verify the signature, an attacker can forge any payload, set sub to a privileged user, and gain unauthorized access.

JWT Tokens‑Specific Detection

Detecting identification failures requires checking that the server actually validates the token’s integrity and claims. Manual testing can be done with tools like jwttool or Burp Suite extensions, but an automated scanner such as middleBrick performs these checks as part of its unauthenticated black‑box scan.

When you submit an API endpoint to middleBrick, it runs the following JWT‑specific probes in parallel:

  • Signature verification – sends a token with a random signature and expects a 401/403 response.
  • Algorithm confusion – attempts to sign a token with the none algorithm and with HS256 using a guessed secret.
  • Expiration and nbf checks – replays a token with a past exp or future nbf and verifies rejection.
  • kid header injection – supplies a kid that points to an attacker‑controlled JWK set and looks for acceptance.
  • Missing audience (aud) and issuer (iss) validation.

If any of these probes succeed, middleBrick reports a finding under the Identification Failure sub‑category, providing the exact request/response, severity (usually High), and remediation guidance.

Example CLI usage:

middlebrick scan https://api.example.com/auth/token

The output includes a JSON block such as:

{
  "findings": [
    {
      "id": "JWT-001",
      "title": "Missing JWT signature verification",
      "severity": "high",
      "description": "The endpoint accepted a token with an invalid signature.",
      "remediation": "Verify the token signature using a trusted secret or public key before using its claims."
    }
  ]
}

This gives developers a concrete, actionable starting point for fixing the issue.

JWT Tokens‑Specific Remediation

Fixing identification failures consists of ensuring that every JWT is cryptographically validated and that its required claims are checked before the token is trusted. Below are language‑specific examples that use the libraries’ built‑in validation features.

Node.js (jsonwebtoken)

Replace the insecure decode call with verify, specifying the algorithm set and checking the standard claims.

const jwt = require('jsonwebtoken');
const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET; // strong, random secret

app.get('/api/data', (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).send('Missing or malformed token');
  }
  const token = authHeader.split(' ')[1];

  try {
    const payload = jwt.verify(token, ACCESS_TOKEN_SECRET, {
      algorithms: ['HS256'],          // enforce HS256 only
      issuer: 'my-service',           // optional: validate iss
      audience: 'my-api',             // optional: validate aud
      // exp and nbf are validated by default
    });
    // payload.sub, payload.role, etc. are now trustworthy
    req.user = payload;
    next();
  } catch (err) {
    // err.name === 'JsonWebTokenError' or 'TokenExpiredError'
    return res.status(401).send('Invalid token');
  }
});

Python (PyJWT)

Use jwt.decode with the algorithms parameter and enable claim validation.

import jwt
import os
SECRET_KEY = os.environ['ACCESS_TOKEN_SECRET']

def protect(endpoint):
    def wrapper(*args, **kwargs):
        auth = request.headers.get('Authorization')
        if not auth or not auth.startswith('Bearer '):
            return jsonify({'error': 'Missing token'}), 401
        token = auth.split(' ')[1]
        try:
            payload = jwt.decode(
                token,
                SECRET_KEY,
                algorithms=['HS256'],
                issuer='my-service',
                audience='my-api'
                # exp, nbf validated automatically
            )
            request.user = payload
            return endpoint(*args, **kwargs)
        except jwt.ExpiredSignatureError:
            return jsonify({'error': 'Token expired'}), 401
        except jwt.InvalidTokenError:
            return jsonify({'error': 'Invalid token'}), 401
    return wrapper

@app.route('/api/data')
@protect
def get_data():
    return jsonify({'msg': 'safe data'})

.NET (System.IdentityModel.Tokens.Jwt)

Configure TokenValidationParameters in the authentication middleware.

var tokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidIssuer = "my-service",
    ValidateAudience = true,
    ValidAudience = "my-api",
    ValidateLifetime = true,          // checks exp and nbf
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
        Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET"))),
    ValidateIssuerSigningKey = true,
    // Require HS256 only
    RequireSignedTokens = true,
    // Prevent 'none' algorithm
    RequireExpirationTime = true
};

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = tokenValidationParameters;
    });

// In controllers, use [Authorize] attribute
[ApiController]
[Route("api/[data]")]
public class DataController : ControllerBase
{
    [Authorize]
    [HttpGet]
    public IActionResult Get() => Ok(User.Claims);
}

After applying these changes, run middleBrick again. The scanner should now report that the JWT signature, expiration, and claim checks are enforced, and the finding will disappear.

Frequently Asked Questions

What is the most common JWT identification failure that leads to authentication bypass?
The most common issue is missing signature verification—accepting a token without checking its cryptographic signature, which lets an attacker forge any payload and impersonate any user.
How does middleBrick detect a JWT that accepts the 'none' algorithm?
middleBrick sends a token whose alg header is set to 'none' and a valid‑looking payload. If the API processes the token and returns a successful response, the scanner flags it as a high‑severity identification failure.