HIGH brute force attackjwt tokens

Brute Force Attack with Jwt Tokens

How Brute Force Attack Manifests in JWT Tokens

A JSON Web Token (JWT) consists of three base64url‑encoded parts: header, payload, and signature. The signature is created by signing the header and payload with a secret (HMAC algorithms) or a private key (RSA/ECDSA). If the secret is weak or predictable, an attacker can perform an offline brute‑force attack: they capture a valid token, then repeatedly guess the secret until the reproduced signature matches. Once the secret is discovered, the attacker can forge any token for any user, effectively bypassing authentication.

Another brute‑force vector targets the token’s claims. Applications sometimes rely on predictable values in the payload (e.g., a numeric user ID) to authorize requests. An attacker can enumerate possible IDs, tamper with the payload, and re‑sign the token if they have guessed the secret or if the server mistakenly accepts an unsigned token (none algorithm). This is especially dangerous when the JWT validation code does not enforce the algorithm, allowing an attacker to downgrade to "none" and then brute‑force the payload.

Real‑world examples include CVE‑2018-0114 (a weak secret in a popular Java JWT library) and CVE‑2022-23529 (a server that accepted "none" algorithm tokens, enabling brute‑force claim manipulation). Both illustrate how insufficient secret strength and lax algorithm validation turn JWTs into a target for brute‑force attacks.

// Example of a weak secret that invites brute force
const jwt = require('jsonwebtoken');
const weakSecret = 'secret123'; // easily guessable
const token = jwt.sign({ userId: 42, role: 'user' }, weakSecret, { expiresIn: '1h' });
console.log(token);
// An attacker with this token can try millions of HMAC‑SHA256 guesses offline
// to recover 'secret123' and then forge arbitrary tokens.

JWT Tokens‑Specific Detection

middleBrick’s unauthenticated black‑box scan includes checks that reveal the conditions that enable JWT brute force. It examines the token endpoint (often /auth/login or /token) for:

  • Use of HS256 with a short or common secret (detected by trying a dictionary of likely secrets against a captured token).
  • Missing or excessive expiration (exp) claims, which increase the window for offline guessing.
  • Acceptance of the "none" algorithm (indicating missing algorithm enforcement).
  • Lack of audience (aud) or issuer (iss) validation, making token substitution easier.

When a scan finds a JWT with a weak secret, middleBrick reports it under the "Authentication" category with a severity of **High**, providing the exact token sample and the guessed secret (if recovered within the scan’s time box). The report also lists remediation guidance.

You can trigger this check from the terminal using the middleBrick CLI:

# Install the CLI (npm)
npm i -g middlebrick
# Scan an API endpoint that issues JWTs
middlebrick scan https://api.example.com/auth/login
# Output includes a JSON field like:
# "jwtBruteForceRisk": {
#   "detected": true,
#   "secretGuess": "s3cr3t",
#   "recommendation": "Replace HS256 secret with a cryptographically random value of at least 32 bytes."
# }

JWT Tokens‑Specific Remediation

Defending against JWT brute force requires strengthening the secret, limiting token lifetime, and enforcing strict validation. Below are language‑agnostic practices with concrete code snippets for Node.js (jsonwebtoken) and Java (jjwt).

1. Use a strong, randomly generated secret (or asymmetric keys). For HMAC, the secret should be at least 32 bytes of entropy.

// Node.js – generate a strong secret at startup
const crypto = require('crypto');
const jwtSecret = crypto.randomBytes(32).toString('base64'); // store securely, e.g., in env var
const token = require('jsonwebtoken').sign({ userId: 123 }, jwtSecret, { algorithm: 'HS256', expiresIn: '15m' });

2. Prefer asymmetric algorithms (RS256/ES256) where the private key is held only by the issuer and the public key is used for verification. This eliminates the need to share a secret.

// Java (jjwt) – RS256 signing
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
String jwt = Jwts.builder()
        .setSubject("alice")
        .setExpiration(new Date(System.currentTimeMillis() + 900_000)) // 15 min
        .signWith(privateKey, SignatureAlgorithm.RS256)
        .compact();

3. Enforce short expiration and implement refresh‑token rotation. This limits the usefulness of any guessed secret.

// Express middleware that checks exp and rejects tokens older than 5 min
function jwtAuth(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.sendStatus(401);
  try {
    const payload = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] });
    // additional check: token issued‑at (iat) not older than 5 min
    if (Date.now() - payload.iat * 1000 > 300000) return res.sendStatus(401);
    req.user = payload;
    next();
  } catch (err) {
    return res.sendStatus(401);
  }
}

4. Validate the algorithm header explicitly; never accept "none". Most libraries do this by default when you specify the algorithm list, but double‑check your code.

5. Add audience (aud) and issuer (iss) claims and verify them; this prevents token substitution across services.

6. Deploy rate limiting on the token‑issuing endpoint (e.g., 5 attempts per username/IP per minute) to throttle online guessing attempts. middleBrick’s Rate Limiting check will flag missing limits.

By combining a strong secret (or key pair), short lifetimes, strict validation, and endpoint throttling, you remove the practical feasibility of brute‑forcing JWTs.

Frequently Asked Questions

Can middleBrick recover the exact secret used to sign a JWT?
middleBrick attempts a limited dictionary‑based guess of the secret during its 5‑15 second scan. If the secret is weak enough to be found within that time, the report will include the guessed secret; otherwise it will note that a strong secret appears to be in use and advise using a cryptographically random value of sufficient length.
Does enabling the "none" algorithm always lead to a brute‑force vulnerability?
Yes. Accepting the "none" algorithm removes the cryptographic signature, allowing an attacker to alter the payload (including claims like userId or role) without needing to guess a secret. This is effectively a brute‑force‑friendly scenario because the attacker can enumerate arbitrary claims until they find one that grants privileged access.