Brute Force Attack in Express with Api Keys
Brute Force Attack in Express with Api Keys — how this specific combination creates or exposes the vulnerability
A brute force attack against an Express API that relies solely on API keys creates a predictable authentication surface. Because API keys are typically long-lived credentials passed in headers, query parameters, or cookies, attackers can systematically iterate through key values or attempt many candidate keys to discover valid ones. Without explicit rate limiting on key verification endpoints, each request consumes minimal server resources, allowing rapid credential testing. This pattern is common when API keys act as the primary gatekeeper without additional factors, and it maps to the BFLA / Privilege Escalation and Rate Limiting checks in middleBrick’s 12 security checks.
Express applications often expose two related risks. First, if authentication is implemented as a simple middleware check like verifying a key against a static list or a database lookup without throttling, an attacker can send many requests per second to enumerate valid keys. Second, if authorization does not enforce scope or ownership checks after authentication, a discovered key might grant excessive permissions, leading to BOLA / IDOR or privilege escalation. These issues are detectable by middleBrick’s parallel checks, which analyze both the OpenAPI specification and runtime behavior to identify missing rate controls and weak authorization logic.
Consider an Express route that authenticates requests using an api-key header:
const express = require('express');
const app = express();
const VALID_KEYS = ['sk_live_abc123', 'sk_test_xyz789'];
function authenticate(req, res, next) {
const key = req.headers['api-key'];
if (!key) {
return res.status(401).json({ error: 'API key missing' });
}
if (VALID_KEYS.includes(key)) {
req.apiKey = key;
return next();
}
res.status(403).json({ error: 'Invalid API key' });
}
app.get('/admin', authenticate, (req, res) => {
res.json({ secret: 'admin data' });
});
app.listen(3000);
This snippet lacks any request-rate throttling on the authentication check. An attacker can repeatedly send requests with different key values to the /admin endpoint without meaningful slowdown, making online brute force feasible. Because the scan is unauthenticated, middleBrick’s black-box testing can detect the absence of rate limiting and highlight the elevated risk. Additionally, if key validation logic is centralized but reused across routes without per-route authorization checks, Property Authorization gaps may appear, where one valid key accesses endpoints it should not.
To align with best practices and reduce the attack surface, combine API key authentication with explicit rate limiting and strict authorization checks. This reduces the feasibility of online brute force and constrains the impact of a discovered key. middleBrick’s findings include prioritized guidance and remediation steps tied to frameworks such as OWASP API Top 10 and PCI-DSS, helping you address these weaknesses systematically.
Api Keys-Specific Remediation in Express — concrete code fixes
Remediation focuses on two controls: rate limiting at the endpoint or global level, and ensuring authorization validates both the key and the permitted actions for that key. Implementing a token bucket or sliding window rate limiter ensures that repeated requests from a single source are throttled, which directly mitigates brute force attempts. Authorization should be scoped so that each key only accesses routes and operations it is explicitly allowed to use, preventing privilege escalation.
Below is a hardened Express example that incorporates rate limiting using express-rate-limit and a more granular authorization function that checks key-to-role mappings:
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();
const keyToRole = {
'sk_live_abc123': 'admin',
'sk_live_def456': 'read',
};
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each key to 100 requests per window
message: { error: 'Too many requests, try again later.' },
standardHeaders: true,
legacyHeaders: false,
});
function authenticate(req, res, next) {
const key = req.headers['api-key'];
if (!key) {
return res.status(401).json({ error: 'API key missing' });
}
if (!keyToRole[key]) {
return res.status(403).json({ error: 'Invalid API key' });
}
req.role = keyToRole[key];
next();
}
function requireRole(requiredRole) {
return (req, res, next) => {
if (req.role !== requiredRole) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
app.use(authLimiter);
app.get('/admin', authenticate, requireRole('admin'), (req, res) => {
res.json({ secret: 'admin data' });
});
app.get('/data', authenticate, requireRole('read'), (req, res) => {
res.json({ data: 'public data' });
});
app.listen(3000);
This approach applies a global rate limit on authentication attempts, reducing the feasibility of brute force. It also introduces role-based authorization so that even if a key is discovered, its access is restricted. For broader coverage, you can apply the rate limiter to specific routes or use a more sophisticated store for tracking attempts in distributed environments.
middleBrick’s scans can verify whether these controls are present and correctly configured by correlating the OpenAPI spec with runtime behavior. If your organization requires continuous assurance, the middleBrick Pro plan includes continuous monitoring and configurable scanning schedules so that regressions are detected early. The GitHub Action can fail builds when security scores drop below your defined threshold, and the CLI allows you to automate checks from your terminal with commands like middlebrick scan <url>.