Use After Free in Express with Bearer Tokens
Use After Free in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Use After Free (UAF) in an Express API typically occurs when an object (such as a request-scoped user context or token payload) is deallocated or made unavailable while a reference to it is still in use. When combined with Bearer Tokens, this can happen if token parsing, validation, or attachment to the request object is handled in a way that leaves dangling references after the underlying data is freed or overwritten.
In Express, a common pattern is to verify a Bearer Token and attach decoded claims to req.user. If the token payload or an associated data structure is reused, mutated, or cleared before the request processing finishes, code that later reads req.user may access freed memory or inconsistent state. For example, in a middleware chain, if an earlier middleware frees or overwrites a user object and a downstream middleware or route handler still tries to read properties like req.user.sub, the application may behave unpredictably, potentially exposing other users’ data or causing crashes.
Consider an Express route that decodes a Bearer Token and stores a reference to the payload on the request object:
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = authHeader.substring(7);
try {
const decoded = verifyToken(token); // hypothetical verifyToken function
req.user = decoded; // attaches payload to the request
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
If the object held by req.user is later mutated or cleared—intentionally or due to a bug in shared objects—subsequent middleware may read invalid data. This becomes more likely when token payloads contain references to external resources (e.g., user records) that are re-fetched or re-assigned within the same request lifecycle. An attacker could exploit timing differences or improper scoping to cause the server to read memory that was intended to be released, leading to information disclosure or instability.
Another scenario involves asynchronous operations. If a token is verified and req.user is set, but an async operation (such as a database call) is deferred and the request object is reused or the user reference is overwritten before the async callback executes, the callback may operate on a stale or invalid user object. This pattern is common in Express apps that perform multiple asynchronous checks after authentication.
To map findings to industry standards, UAF in this context aligns with OWASP API Top 10 categories such as Broken Object Level Authorization (BOLA) and Security Misconfiguration. It can also intersect with improper validation of Bearer Tokens, where missing or weak token validation leads to authorization bypasses. Compliance frameworks like PCI-DSS and SOC2 require proper session and credential management, which includes ensuring that authenticated user contexts remain valid and isolated for the duration of the request.
middleBrick scans can detect risky patterns such as missing validation on Bearer Tokens, missing scope checks, and improper handling of decoded payloads. By correlating runtime behavior with OpenAPI specifications, it can highlight endpoints where authentication and authorization logic may leave references in an unstable state, enabling developers to review and refactor the request lifecycle.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
Remediation focuses on ensuring that request-sc、用户 data derived from Bearer Tokens is immutable for the duration of the request and that no stale references are retained. Use local variables rather than attaching sensitive data directly to req.user when possible, or ensure that attached data is defensively copied. Validate and verify tokens synchronously before attaching any data, and avoid reusing objects across middleware.
Here is a secure pattern for Bearer Token handling in Express:
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const token = authHeader.substring(7);
let decoded;
try {
decoded = verifyToken(token); // synchronous verification for this example
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
// Create a new object to avoid accidental mutation or reuse
const userContext = {
sub: decoded.sub,
roles: Array.isArray(decoded.roles) ? [...decoded.roles] : [],
scope: decoded.scope ? decoded.scope.split(' ') : [],
}};
// Attach a copy to avoid external mutation
req.authContext = userContext;
next();
}
In this example, verifyToken should use a trusted library (e.g., jsonwebtoken) with a strong secret or public key, and the decoded payload is copied into a new object. By not directly assigning the decoded payload to req.user, you reduce the risk of unintended mutations or cross-request contamination.
For asynchronous workflows, capture the necessary values in closure-safe variables before starting async operations:
app.get('/profile', authenticate, async (req, res) => {
const { sub, roles } = req.authContext; // captured values
try {
const userData = await db.getUser(sub);
// roles can be checked here for authorization
if (!roles.includes('read_profile')) {
return res.status(403).json({ error: 'Forbidden' });
}
res.json({ user: userData, roles });
} catch (err) {
res.status(500).json({ error: 'Internal server error' });
}
});
This pattern ensures that the values used in the async callback are fixed at the time of the request and not affected by later mutations to shared request objects. It also supports principle of least privilege by checking roles and scopes extracted and copied during authentication.
Additionally, enforce strict token validation: require an issuer (iss), audience (aud), expiration (exp), and not-before (nbf) claims. Rotate signing keys and prefer asymmetric algorithms (e.g., RS256) to limit the impact of a compromised key. middleBrick’s Bearer Token checks can verify the presence of these claims and flag endpoints that accept unsigned or weakly validated tokens.
For compliance, ensure that token handling does not leak sensitive claims in logs or error responses. Use structured logging that excludes PII and API keys, and apply consistent error messages to avoid leaking authentication state details. The Pro plan’s continuous monitoring can help detect configurations where tokens are accepted without proper verification or where endpoints lack required scope checks.