Clickjacking in Sails (Javascript)
Clickjacking in Sails with Javascript — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side attack where an attacker tricks a user into interacting with invisible or disguised UI elements. In a Sails application that serves HTML views and uses JavaScript heavily on the client, misconfigured HTTP headers and insecure view templates can enable clickjacking. Sails does not set frame-denying headers by default, so if a page is rendered without an explicit X-Frame-Options or Content-Security-Policy (frame-ancestors), an attacker can embed the app in an <iframe> and overlay interactive elements to hijack clicks.
When Sails serves pages that include inline JavaScript or dynamically rendered partials, the risk increases if those scripts manipulate DOM elements that could be overlaid. For example, a vulnerable Sails view might render a form with an unprotected <button> or link, while an attacker’s page uses CSS and JavaScript to position an invisible iframe and map click coordinates to sensitive actions (such as approving a transaction or changing an email). Because Sails often uses JavaScript on the frontend to bind events or make AJAX requests, an attacker can synchronize overlays with user gestures, making the interaction seamless.
Additionally, if Sails APIs or controllers expose endpoints that perform state-changing operations without verifying the request origin (e.g., missing CSRF tokens for unsafe methods), clickjacking becomes more impactful. Even when using JavaScript frameworks client-side, server-side decisions must not rely solely on the assumption that requests originate from the intended UI. Without proper anti-clickjacking headers and secure-by-default view configurations, Sails applications remain exposed, particularly when JavaScript manipulates the DOM in ways that can be obscured by overlays.
Javascript-Specific Remediation in Sails — concrete code fixes
Remediation focuses on HTTP headers, CSP, and safe JavaScript practices. Headers should be set globally in Sails to prevent framing, and CSP should restrict frame ancestors. For JavaScript, avoid unsafe practices that make UI elements easy to overlay, and ensure event handlers validate context.
1. Set anti-clickjacking headers
Configure Sails to send X-Frame-Options and Content-Security-Policy headers. In a Sails app, this is commonly done in config/http.js.
module.exports.http = {
middleware: {
order: ['startRequestTimer', 'cookieParser', 'session', 'mySecurityHeaders', 'bodyParser', 'handleBodyParserError', 'compress', 'methodOverride', 'poweredBy', '$custom', 'www', 'router'],
mySecurityHeaders: function(req, res, next) {
res.set('X-Frame-Options', 'DENY');
res.set(
'Content-Security-Policy',
"default-src 'self'; frame-ancestors 'none';"
);
return next();
}
}
};
2. Secure JavaScript event handling
Ensure that critical UI interactions are not easily overlapped. Use pointer events with hit-testing and avoid placing interactive elements under transparent or absolutely positioned layers. If you must use overlays, validate that they are intentional.
document.addEventListener('click', function(e) {
// Example: ensure clicks on critical buttons are not intercepted
const button = e.target.closest('.secure-action');
if (!button) return;
// Verify the element is not obscured by checking bounding rect
const rect = button.getBoundingClientRect();
if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) {
console.warn('Click intercepted by overlay');
return;
}
// Proceed with action
fetch('/api/secure-action', { method: 'POST', credentials: 'include' });
});
3. Avoid unsafe HTML in partials
When rendering dynamic content in Sails views (e.g., EJS), ensure you do not inadvertently expose click targets via unsanitized user input. Use strict escaping and avoid injecting raw HTML that could be manipulated by attackers.
<% if (userProvidedContent) { %>
<div><%- userProvidedContent %></div> <!-- XSS risk, avoid -->
<% } else { %>
<div><%= userProvidedContent %></div> <!-- Escaped output -->
<% } %>
4. Protect AJAX endpoints
Ensure that state-changing AJAX requests include CSRF tokens and validate the Origin header. Sails has built‑in CSRF protection for forms; for APIs, enforce origin checks.
module.exports.security = {
csrf: true,
csrfCookie: 'csrfToken',
csrfHeader: 'x-csrf-token'
};
// In a controller
updateEmail: async function(req, res) {
if (!req.isOriginAllowed('https://trusted.example.com')) {
return res.unauthorized('Invalid origin');
}
// proceed with update
}