In Express.js, middleware executes in registration order. When express-session is
registered before express.static, session cookies are attached to every response, including static assets like images, CSS, and
JavaScript files.
This creates two exploitation paths:
Cookie exposure: Proxies, load balancers, or CDNs may cache static asset responses. If the cached response includes a
Set-Cookie header, that session identifier gets served to other users, allowing attackers to hijack sessions without any direct
interaction with the victim.
Cross-site attack vectors: Attaching sessions to static assets forces the server to process session logic for every image or
script. Depending on the business logic, this mechanism can be used by attackers as exploitation "gadgets".
For example, an attacker can use an
<img> or <iframe> on their own malicious site pointing to one of these static assets. After forcing a target
user’s browser to load https://your-site.example.com/icon.png, the attacker can
attempt to trigger some specific session-based behaviors, such renewing a session, to probe your server.
This is a side-channel attack called a
Cross-Site Leak (XS-Leak).
If an attacker gains access to a user’s session cookie, they can impersonate that user and perform actions on their behalf. This means accessing private data, making unauthorized transactions, or escalating privileges within the application. For applications handling sensitive information such as financial data, healthcare records, or personal information, a single compromised session can lead to significant data breaches, regulatory violations, and reputational damage.
The following code is vulnerable because the session middleware is registered before the static file middleware, causing session cookies to be sent with every static asset response.
const express = require('express');
const session = require('express-session');
const path = require('path');
const app = express();
app.use(session({
secret: process.env.SESSION_SECRET
}));
app.use(express.static(path.join(__dirname, 'app/assets'))); // Noncompliant
const express = require('express');
const session = require('express-session');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'app/assets')));
app.use(session({
secret: process.env.SESSION_SECRET
}));
To fix this weakness, apply the principle of least privilege by ensuring session data is only attached to responses that strictly require it. Developers must guarantee that session cookies are never exposed to shared caches or Content Delivery Networks (CDNs), which is achieved by correctly sequencing middleware. By registering express.static before the session middleware, requests for public assets are intercepted and served immediately; this prevents the session logic from ever executing for those files, effectively eliminating the session-based attack surface for static resources.