Clickjacking in Express with Bearer Tokens
Clickjacking in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side UI redress attack where an attacker tricks a user into interacting with a hidden or disguised element inside an invisible or overlapped frame. When an Express application issues access tokens via the Authorization header using the Bearer scheme, clickjacking can amplify the risk of token leakage and unauthorized actions even though the token itself is not transmitted in the page HTML.
In the Express + Bearer Token context, clickjacking becomes relevant in two primary scenarios. First, if an authenticated page makes authorized API requests using tokens stored client-side (for example in JavaScript-accessible storage or via an embedded widget), an attacker can load the target page inside a transparent iframe and overlay interactive controls (like buttons or links) that cause the user to perform privileged actions while authenticated. Because the browser automatically includes the Bearer token in the Authorization header for same-origin or credentialed cross-origin requests (depending on withCredentials settings), those forged interactions execute with the victim’s privileges.
Second, if the Express app embeds third-party content or exposes an OAuth authorization page inside an iframe without proper framing defenses, an attacker can frame the authorization flow and capture resulting tokens or trick the user into granting permissions. Even though Bearer tokens are typically transmitted in headers, UI interactions that lead to token issuance or use can be hijacked via clickjacking if frame ancestors are not restricted.
The presence of Bearer Tokens does not prevent clickjacking; it changes the impact. An attacker may not directly steal the token from the header, but they can leverage the authenticated session to perform actions such as changing email, updating permissions, or invoking sensitive endpoints that rely on the token for authorization. This is particularly dangerous for endpoints that perform sensitive operations based on user context rather than explicit token introspection.
To illustrate, consider an Express route that updates a user’s email. If this route relies solely on a Bearer token in the Authorization header and lacks CSRF mitigation or frame restrictions, an attacker can craft a page that tricks the user’s browser into making a PUT request to that endpoint while the user is authenticated:
<!doctype html>
<html>
<head><title>Profile</title></head>
<body>
<h1>Update Email</h1>
<form id="steal" action="https://api.example.com/v1/account/email" method="PUT" style="display:none">
<input type="email" name="email" value="[email protected]" />
<button type="submit">Save</button>
Although the token is not in the DOM, the authenticated request can still succeed if the server does not enforce anti-CSRF protections or strict frame policies. Clickjacking in this scenario leverages the user’s valid authentication state, highlighting the need for defense-in-depth when Bearer Tokens are used.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
Remediation focuses on preventing unauthorized UI interactions and ensuring that token usage does not bypass standard CSRF and framing protections. The following measures reduce clickjacking risk in Express applications that use Bearer Tokens.
1. Set Content-Security-Policy frame-ancestors to disallow embedding
Use helmet to restrict which origins can embed the page. This prevents the application from being loaded in iframes on attacker-controlled sites:
const helmet = require('helmet');
app.use(helmet.frameguard({ action: 'deny' })); // or { action: 'sameorigin' }
app.use(helmet.contentSecurityPolicy({
useDefaults: true,
directives: {
frameAncestors: ["'none'"]
}
}));
2. Enforce SameSite and Secure attributes on cookies if session tokens are used alongside Bearer patterns
Even if Bearer tokens are primarily in headers, cookies might still carry session context. Apply strict cookie policies:
app.use(session({
cookie: {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600000
},
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
}));
3. Require CSRF tokens for state-changing operations even when Bearer tokens are present
If your API serves web views or cookies are present, synchronize CSRF protection with Bearer token usage. For API clients that manage their own token storage, ensure cross-origin requests do not automatically send credentials:
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
app.use(cookieParser());
const csrfProtection = csrf({ cookie: true });
app.post('/v1/account/email', csrfProtection, (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'unauthorized' });
// verify token, then process request
res.json({ ok: true });
});
4. Validate Origin and Referer headers for sensitive endpoints
Add middleware to ensure requests originate from expected sources, reducing the likelihood of forged requests from embedded contexts:
app.use((req, res, next) => {
const allowedOrigin = process.env.APP_ORIGIN;
const origin = req.headers.origin;
const referer = req.headers.referer || '';
if (origin && origin !== allowedOrigin) {
return res.status(403).json({ error: 'invalid origin' });
}
if (referer && !referer.startsWith(allowedOrigin)) {
return res.status(403).json({ error: 'invalid referer' });
}
next();
});
5. Serve APIs with appropriate CORS configuration
Explicitly define allowed origins and avoid wildcard origins when credentials or tokens are involved:
const cors = require('cors');
const corsOptions = {
origin: process.env.TRUSTED_ORIGIN,
credentials: true
};
app.use(cors(corsOptions));
6. Avoid embedding sensitive pages in external iframes
Ensure that pages that perform sensitive actions are not served in a way that encourages embedding. The helmet frameguard setting above ensures browsers enforce the policy.
These steps align with defense-in-depth: frame restrictions prevent UI manipulation, CSRF protections block unauthorized actions, and strict headers reduce the attack surface even when Bearer tokens are used for authentication.