Clickjacking in Loopback with Mutual Tls
Clickjacking in Loopback with Mutual Tls — 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 transparent iframe. In Loopback applications that use Mutual TLS (mTLS), the presence of mTLS does not prevent clickjacking because mTLS secures the transport channel between client and server—it does not enforce how the response is rendered or embedded by the client browser. An authenticated mTLS session can still return HTML pages that include vulnerable iframe directives, and the browser will render them as long as the origin is allowed to frame the content.
When mTLS is enforced, the server typically presents a client certificate challenge during the TLS handshake and may populate request properties (e.g., req.client.cert) with identity details. While this helps authenticate the client, it does not prevent the server from sending clickjack-vulnerable headers or markup. If the application sets X-Frame-Options only for selected endpoints or omits it for admin or status pages, an attacker can craft a page that embeds a sensitive Loopback endpoint (e.g., a confirmation or action page) inside an iframe and overlay interactive elements to hijack user actions. The mTLS layer ensures the request comes from a trusted client certificate, but it does not stop the browser from honoring the framing rules defined by HTTP headers or CSP frame-ancestors directives.
For example, consider a Loopback endpoint protected by mTLS that renders a state-changing form. An attacker’s page could contain:
<iframe src="https://api.example.com/admin/transfer" style="opacity:0; position:absolute;"></iframe>
<button style="position:relative; z-index:1;">Click me for a prize</button>
If the endpoint lacks Content-Security-Policy: frame-ancestors 'none' or X-Frame-Options, the browser loads the form inside the invisible iframe, and the user’s click triggers the action with their authenticated mTLS session. middleBrick can detect this by scanning the unauthenticated attack surface (including endpoints served over mTLS) for missing frame-protection headers and provides remediation guidance to set strict CSP frame-ancestors and X-Frame-Options. Note that mTLS should be complemented with these framing defenses to achieve defense-in-depth.
Mutual Tls-Specific Remediation in Loopback — concrete code fixes
To mitigate clickjacking in a Loopback application with Mutual TLS, you must enforce framing restrictions independently of mTLS. Combine secure TLS configuration with explicit HTTP headers and CSP policies. Below are concrete steps and code examples.
- Set global HTTP headers to prevent framing. Use a Loopback middleware to add
Content-Security-Policywithframe-ancestorsandX-Frame-Options:
const loopback = require('loopback');
const app = loopback();
// Middleware to set framing headers
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; frame-ancestors 'none'"
);
next();
});
// Your existing mTLS-enabled server setup
// Assuming the server is configured to request client certificates
app.start = function() {
const server = app.listen(3000, () => {
console.log('Server listening on port 3000');
});
return server;
};
- If you use
loopback-component-exploreror REST API endpoints, restrict framing for explorer and admin UI routes specifically:
const path = require('path');
app.use('/explorer', (req, res, next) => {
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; frame-ancestors 'self'"
);
next();
});
- Ensure your mTLS configuration in
server.jsrequests client certificates and validates them, but do not rely on it for framing controls:
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert'),
ca: fs.readFileSync('ca.cert'),
requestCert: true,
rejectUnauthorized: true,
};
const httpsServer = https.createServer(options, app);
httpsServer.listen(8443);
- For SPAs or embedded dashboards, use strict CSP instead of
X-Frame-Options(the latter is ignored if CSP frame-ancestors is present):
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'"
);
next();
});
These changes ensure that even when mTLS provides strong client authentication, the application does not expose endpoints to clickjacking via iframes. middleBrick’s scans will surface missing headers and frame-ancestors violations so you can apply these fixes.