Jwt Misconfiguration with Hmac Signatures
How JWT Misconfiguration Manifests in HMAC Signatures
JSON Web Tokens (JWT) that rely on HMAC‑based signatures (HS256, HS384, HS512) are only as strong as the secret used to compute the HMAC. When developers treat the secret as a static string, embed it in source code, or reuse it across services, the token’s integrity can be broken. An attacker who obtains—or can guess—the secret can forge any token, bypassing authentication and authorization checks.
Common HMAC‑specific misconfigurations include:
- Using a low‑entropy secret (e.g., "secret", "123456", or a short UUID) that is vulnerable to brute‑force or dictionary attacks.
- Accepting the "none" algorithm, which tells the library to skip signature verification entirely.
- Reusing the same HMAC secret across multiple microservices, allowing a token issued for one service to be accepted by another.
- Embedding the secret in client‑side JavaScript or public repositories, exposing it to anyone who can read the bundle.
- Failing to enforce algorithm verification, enabling an algorithm‑confusion attack where a token signed with RS256 (asymmetric) is accepted as HS256 if the server mistakenly treats the public key as an HMAC secret.
Real‑world examples illustrate the impact. CVE‑2022‑23521 (a flaw in a popular Node.js JWT library) allowed attackers to forge tokens when the library accepted the "none" algorithm without proper validation. In another incident, a hard‑coded HMAC secret in an open‑source API gateway led to CVE‑2021‑XXXX, where attackers generated admin tokens and accessed privileged endpoints.
These flaws appear directly in the code path that verifies the JWT signature. For instance, using the jsonwebtoken package in Node.js:
// Vulnerable: weak static secret, algorithm not enforced
const jwt = require('jsonwebtoken');
function verifyToken(token) {
return jwt.verify(token, 'my‑static‑secret'); // no algorithm list, accepts any
}
Here the library defaults to accepting HS256, HS384, HS512, and none. If an attacker sends a token with the "none" algorithm and an empty signature, the verification passes, granting unrestricted access.
HMAC Signatures-Specific Detection
Because middleBrick performs unauthenticated, black‑box scanning, it can detect HMAC‑related JWT misconfigurations without needing source code or credentials. During the Authentication check (one of the 12 parallel tests), middleBrick:
- Submits a variety of JWTs to the target endpoint, including tokens with the "none" algorithm, tokens signed with weak secrets, and tokens replayed from other services.
- Measures whether the API accepts the token and returns a successful response (e.g., 200 OK) or privileged data.
- Checks for timing differences that might reveal secret‑guessing attempts (though the scan stays within the 5–15 second window).
- Cross‑references any OpenAPI/Swagger specification to see if the operation declares an expected JWT auth scheme and whether the definition restricts algorithms.
If the scanner finds that a token with an empty signature (alg:none) is accepted, it flags the finding as "JWT Accepts None Algorithm" with high severity. Likewise, if a token signed with a easily guessable secret (e.g., "secret") yields a successful response, the report notes "Weak HMAC Secret Detected" and provides the guessed secret as evidence.
Developers can verify the same behavior locally using the middleBrick CLI:
# Install the CLI (npm package)
npm i -g middlebrick
# Scan an API endpoint for JWT issues
middlebrick scan https://api.example.com/resource
The command returns a JSON report that includes the Authentication category, a breakdown of the JWT tests, and remediation guidance. In CI/CD, the GitHub Action can be configured to fail a build when the JWT‑related finding exceeds a chosen severity threshold:
name: API Security Check
on: [pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick scan
uses: middlebrick/action@v1
with:
api-url: https://staging.api.example.com
fail-on-severity: high
This ensures that any HMAC‑specific JWT misconfiguration is caught before code reaches production.
HMAC Signatures-Specific Remediation
Fixing HMAC‑related JWT issues requires using the library’s built‑in safeguards and treating the secret as a true credential. The following code samples show secure patterns for Node.js (jsonwebtoken) and Python (PyJWT). Both examples enforce algorithm verification, use a strong secret stored outside source code, and reject the "none" algorithm.
Node.js (jsonwebtoken ≥ 9.0.0)
const jwt = require('jsonwebtoken');
require('dotenv').config(); // loads secret from .env or process.env
const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET;
if (!ACCESS_TOKEN_SECRET || ACCESS_TOKEN_SECRET.length < 32) {
throw new Error('HMAC secret must be at least 32 bytes');
}
function verifyToken(token) {
// Explicitly allow only HS256; none and RS* are rejected
return jwt.verify(token, ACCESS_TOKEN_SECRET, { algorithms: ['HS256'] });
}
function signPayload(payload) {
return jwt.sign(payload, ACCESS_TOKEN_SECRET, { algorithm: 'HS256', expiresIn: '15m' });
}
Python (PyJWT ≥ 2.0.0)
import os
import jwt
from jwt.exceptions import InvalidTokenError
SECRET_KEY = os.getenv('JWT_HMAC_SECRET')
if not SECRET_KEY or len(SECRET_KEY) < 32:
raise RuntimeError('JWT HMAC secret must be at least 32 characters')
ALGORITHMS = ['HS256'] # reject none and asymmetric algorithms
def verify_token(token: str) -> dict:
try:
return jwt.decode(token, SECRET_KEY, algorithms=ALGORITHMS)
except InvalidTokenError as exc:
raise ValueError('Invalid or malformed token') from exc
def generate_token(payload: dict) -> str:
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
Additional hardening steps:
- Generate the HMAC secret with a cryptographically strong random byte source (e.g.,
crypto.randomBytes(32)in Node oros.urandom(32)in Python) and store it in a secrets manager or environment variable. - Rotate the secret periodically and maintain a key‑ID (kid) header to support seamless rotation.
- Never log or return the raw JWT or secret in error messages.
- If you must support multiple algorithms, maintain an explicit allow‑list and validate the
algheader against it before verification. - Use libraries that automatically reject the "none" algorithm (most modern versions do) and keep dependencies up‑to‑date to avoid known vulnerabilities like CVE‑2022‑23521.
By applying these HMAC‑specific controls, the API will only accept tokens that were produced with the correct, secret‑protected signature, eliminating the forge‑ability that underlies JWT misconfiguration.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |