Http Request Smuggling in Express with Basic Auth
Http Request Smuggling in Express with Basic Auth — how this specific combination creates or exposes the vulnerability
Http Request Smuggling arises when an Express application processes HTTP requests differently depending on whether they include hop-by-hop headers such as Transfer-Encoding and Content-Length. This becomes especially risky when the application uses HTTP Basic Authentication. Basic Auth credentials are typically sent in the Authorization header, but if request parsing is inconsistent between front-end proxies and the Express server, an attacker can craft a request that is interpreted differently by each hop.
Consider an Express route that reads req.headers['authorization'] and then relies on the body or URL for further logic. If a front-end proxy normalizes or removes ambiguous headers while Express does not, a request with both Content-Length and Transfer-Encoding: chunked can be split-smuggled. The proxy may process the request based on the first Content-Length body, while Express parses the chunked body separately. Because Basic Auth is often treated as trusted once validated, downstream routing or authorization checks may incorrectly assume the request body or path aligns with the authenticated user's intent.
In practical terms, an attacker can smuggle a request so that an authenticated request meant for one resource is interpreted as applying to another. For example, a POST authenticated to /account/change-email could be smuggled so that a secondary, unauthenticated-sensitive operation such as /admin/rotate-keys is processed under the same credentials. Because the smuggling bypasses normal routing or body-parsing boundaries, the Express app may apply authentication checks to the wrong message boundaries, effectively exposing functionality or data that should have been denied.
Exploitation does not require authentication bypass, but the presence of Basic Auth increases impact: the attacker can act under a valid user’s identity while manipulating request boundaries to achieve unauthorized operations. This is a classic example of how protocol-level parsing differences, combined with application-level trust in authentication, create a pathway for unauthorized actions.
Basic Auth-Specific Remediation in Express — concrete code fixes
Remediation focuses on consistent request parsing, strict header handling, and avoiding reliance on header-derived state for routing or authorization decisions. Below are concrete Express patterns that reduce smuggling risk when Basic Auth is used.
1. Enforce a single body-parsing strategy
Do not mix built-in urlencoded parsing with custom stream-based parsing. Use a strict body parser and reject requests with both Content-Length and Transfer-Encoding.
import express from 'express';
import basicAuth from 'basic-auth';
const app = express();
// Reject requests that contain both Content-Length and Transfer-Encoding
app.use((req, res, next) => {
const hasCL = req.headers['content-length'] !== undefined;
const hasTE = req.headers['transfer-encoding'] !== undefined;
if (hasCL && hasTE) {
return res.status(400).send('Invalid headers: Content-Length and Transfer-Encoding');
}
next();
});
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: false }));
2. Validate and normalize the Authorization header early
Extract and verify Basic Auth before using any request-derived data. Do not allow the request path or body to influence authentication evaluation.
import { auth } from 'basic-auth';
app.use((req, res, next) => {
const user = auth(req);
if (!user || !isValidUser(user.name, user.pass)) {
res.set('WWW-Authenticate', 'Basic realm="area"');
return res.status(401).send('Authentication required');
}
// Store normalized identity for downstream use, not header passthrough
req.user = { name: user.name };
next();
});
3. Avoid routing or business logic based on mutable headers
Do not allow header values to dictate routing or method handling. Use explicit route definitions and ensure body parsing completes before authorization checks that depend on request content.
app.post('/account/change-email', (req, res) => {
if (!req.user) {
return res.status(401).send('Unauthorized');
}
const { email } = req.body;
if (!email || !/^[\S@]+$/.test(email)) {
return res.status(400).send('Invalid email');
}
// Perform change using req.user.name, not any header-derived identity
changeEmail(req.user.name, email);
res.send('Email updated');
});
4. Use strict proxy configuration
Ensure front-end proxies normalize or drop ambiguous hop-by-hop headers before requests reach Express. Configure the proxy to reject requests that contain both Content-Length and Transfer-Encoding, and to preserve the Authorization header without alteration.
5. Test with representative smuggling patterns
Verify that Express rejects or correctly processes smuggled forms such as:
// Smuggled request example (should be rejected or handled safely)
POST /account/change-email HTTP/1.1
Host: api.example.com
Content-Length: 24
Transfer-Encoding: chunked
Authorization: Basic dXNlcjpwYXNz
0
POST /admin/rotate-keys HTTP/1.1
host: ignored
With the mitigations above, such requests should either be rejected at the middleware layer or processed only under the explicitly defined route and body parser.