Jwt Misconfiguration in Express with Basic Auth
Jwt Misconfiguration in Express with Basic Auth — how this specific combination creates or exposes the vulnerability
JWT misconfiguration in an Express API that also uses HTTP Basic Authentication can create a layered risk where weaknesses in one mechanism weaken the other. Basic Authentication transmits a base64-encoded username:password pair in the Authorization header on every request; if transmitted over non-TLS transport, these credentials are easily exposed. Even when TLS is used, relying solely on Basic Auth without proper JWT validation can lead to authorization bypass (BOLA/IDOR) or over-privileged access.
Misconfigured JWT handling often involves missing or weak verification of signatures, acceptance of unsigned tokens (alg: none), or overly broad scopes/roles embedded in the token. When combined with Basic Auth, an attacker who intercepts or steals the Basic credentials gains a valid identity, and a misconfigured JWT validation layer may allow that identity to be elevated or impersonated across endpoints. For example, if the server decodes the JWT payload without verifying the issuer (iss) or audience (aud), and maps the Basic Auth username directly to a role in the token, an attacker can reuse Basic credentials with a tampered token to escalate privileges or access other users’ resources (BOLA/IDOR).
Another common pattern is using Basic Auth to retrieve a user record and then issuing a JWT that embeds permissions derived from that record. If token generation does not validate the scope of access and does not bind the token to the authenticated subject securely (e.g., missing or static jti, missing exp, or accepting tokens with overly long lifetimes), the JWT becomes a persistent bearer token. This is especially risky when endpoints inadvertently accept both Authorization: Basic and bearer tokens without strict scheme validation, leading to confused deputy scenarios where the wrong credential is trusted.
Specific OWASP API Top 10 risks amplified by this combination include Broken Object Level Authorization (BOLA/IDOR) when token claims do not enforce per-request ownership checks, and Security Misconfiguration through use of weak algorithms (none) or missing token binding. Because middleBrick tests authentication schemes and input validation in parallel, such misconfig surface quickly in scans, revealing missing signature verification, missing audience validation, or endpoints that process unsigned tokens.
Real-world attack patterns mirror known CVE behaviors: unsigned tokens (alg: none) allow attackers to forge admin claims; missing exp checks enable replay; and overly permissive scope mappings allow horizontal privilege escalation. These issues compound when Basic Auth credentials are used to derive roles without additional context checks, making token validation a single point of failure.
Basic Auth-Specific Remediation in Express — concrete code fixes
To secure Express APIs that use HTTP Basic Authentication alongside JWT handling, enforce strict transport security, validate and scope tokens independently of Basic credentials, and avoid direct mapping of Basic identities into token privileges without verification.
First, require HTTPS for all endpoints and ensure Basic credentials are never logged or exposed in error messages. Use middleware that rejects requests that do not present TLS; do not allow fallback to non-TLS routes.
Second, validate JWTs rigorously before using them for authorization. Use a well-audited library such as jsonwebtoken and verify signature, issuer, audience, and expiration on every request. Do not accept tokens with alg: none; explicitly specify allowed algorithms.
Third, decouple authorization decisions from the Basic identity. After verifying Basic credentials, fetch the user record and then check permissions against a policy engine or database rather than trusting claims derived from the Basic identity alone. Enforce per-request ownership checks to prevent BOLA/IDOR.
Below are concrete, working Express examples that demonstrate secure handling of Basic Auth and JWT verification.
const express = require('express');
const jwt = require('jsonwebtoken');
const basicAuth = require('basic-auth');
const httpsOnly = require('helmet').hsts;
const app = express();
// Enforce HTTPS in production; in development use a TLS terminator
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && !req.secure) {
return res.status(400).json({ error: 'TLS required' });
}
next();
});
// Strict JWT verification with algorithm and audience/issuer checks
function verifyJwt(token) {
const publicKey = process.env.JWT_PUBLIC_KEY; // PEM string
return jwt.verify(token, publicKey, {
algorithms: ['RS256'],
audience: 'https://api.example.com/',
issuer: 'https://auth.example.com/',
});
}
// Middleware: require and validate JWT before route handling
function requireJwt(req, res, next) {
const authHeader = req.headers.authorization || '';
const parts = authHeader.split(' ');
if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {
return res.status(401).json({ error: 'Authorization header must be Bearer ' });
}
try {
const decoded = verifyJwt(parts[1]);
req.user = decoded; // contains sub, scope, roles, jti, exp, etc.
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token', details: err.message });
}
}
// Middleware: validate Basic credentials and derive user context without mapping directly to JWT claims
function validateBasic(req, res, next) {
const user = basicAuth(req);
if (!user || !user.name || !user.pass) {
res.set('WWW-Authenticate', 'Basic realm="API"');
return res.status(401).json({ error: 'Basic credentials required' });
}
// Perform secure lookup (e.g., against a hashed password store)
// Do NOT issue a JWT here based only on Basic identity; issue after full validation
// Example placeholder: const dbUser = await db.findUserByUsername(user.name);
const dbUser = { id: 'u-123', username: user.name, roles: ['user'] }; // simplified
req.basicUser = dbUser;
next();
}
// Example endpoint requiring both schemes: Basic for identification, JWT for authorization
app.get('/v1/profile', validateBasic, requireJwt, (req, res) => {
// Ensure the JWT subject matches the Basic-identified user to prevent BOLA
if (req.user.sub !== req.basicUser.id) {
return res.status(403).json({ error: 'Subject mismatch: token does not match authenticated user' });
}
// Perform ownership/resource checks here before data access
res.json({ profileId: req.user.sub, roles: req.user.roles || [] });
});
app.listen(3000, () => console.log('API listening on port 3000'));
Key points in the example:
- TLS enforcement prevents Basic credentials from traversing the network in clear text.
- JWT verification specifies RS256 and validates audience and issuer to prevent token substitution across realms.
- Basic credentials are used to retrieve user context, but authorization decisions are based on verified JWT claims plus explicit ownership checks, avoiding direct role mapping from Basic identity.
- The endpoint explicitly compares req.user.sub and req.basicUser.id to prevent BOLA/IDOR when tokens are used for authorization.
In continuous monitoring setups (Pro plan), such validation logic is exercised on each scan, and findings like missing token audience validation or missing TLS enforcement appear with remediation guidance. The GitHub Action can fail builds when risk scores exceed your defined thresholds, and the CLI can be integrated into scripts to enforce secure patterns before deployment.
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 |