Comprehensive strategies and techniques to protect your website from clickjacking attacks
Clickjacking prevention is not just a security best practiceβit's essential for protecting your users and maintaining trust in your application. A successful clickjacking attack can lead to financial loss, data breaches, and severe reputational damage.
Prevention requires a multi-layered approach combining server-side headers, client-side protections, and user education. Each layer provides defense-in-depth, ensuring that if one protection mechanism fails, others are still in place to block attacks.
Effective clickjacking prevention requires implementing multiple protection mechanisms. No single solution is foolproof, but together they create a robust defense that makes attacks significantly harder to execute.
The X-Frame-Options header is the original and most widely supported method for preventing clickjacking. It tells browsers whether your site can be embedded in iframes.
DENYCompletely prevents your site from being displayed in any iframe. This is the most restrictive and secure option.
SAMEORIGINAllows your site to be framed only by pages from the same origin (same domain, protocol, and port). Useful for applications that need internal framing.
ALLOW-FROM uriAllows your site to be framed only by specific origins. Note: This is deprecated in many modern browsers.
CSP is the modern, more flexible approach to controlling frame embedding. The frame-ancestors directive replaces X-Frame-Options and provides granular control.
frame-ancestors 'none'Equivalent to X-Frame-Options: DENY. Prevents all framing.
frame-ancestors 'self'Equivalent to X-Frame-Options: SAMEORIGIN. Allows same-origin framing only.
frame-ancestors 'self' https://trusted.comAllows framing by same origin and specific trusted domains.
# X-Frame-Options Header always set X-Frame-Options "DENY" # CSP frame-ancestors Header always set Content-Security-Policy "frame-ancestors 'none';" # Both headers for maximum compatibility Header always set X-Frame-Options "DENY" Header always set Content-Security-Policy "frame-ancestors 'none';"
# X-Frame-Options add_header X-Frame-Options "DENY" always; # CSP frame-ancestors add_header Content-Security-Policy "frame-ancestors 'none';" always; # Both headers for maximum compatibility add_header X-Frame-Options "DENY" always; add_header Content-Security-Policy "frame-ancestors 'none';" always;
const helmet = require('helmet');
// Basic protection
app.use(helmet.frameguard({ action: 'deny' }));
// CSP frame-ancestors
app.use(helmet.contentSecurityPolicy({
directives: {
frameAncestors: ["'none'"],
},
}));
// Both for maximum protection
app.use(helmet({
contentSecurityPolicy: {
directives: {
frameAncestors: ["'none'"],
},
},
frameguard: { action: 'deny' },
}));<?php
// X-Frame-Options
header("X-Frame-Options: DENY");
// CSP frame-ancestors
header("Content-Security-Policy: frame-ancestors 'none';");
// Both headers
header("X-Frame-Options: DENY");
header("Content-Security-Policy: frame-ancestors 'none';");
?>JavaScript frame-busting code can detect when your page is embedded in an iframe and break out of it. This provides an additional layer of protection.
if (top !== self) { top.location = self.location; }if (top !== self) {
try {
top.location = self.location;
} catch (e) {
// Cross-origin restrictions
window.location = 'about:blank';
}
}// Prevent iframe blocking
if (self !== top) {
// Page is in an iframe
if (typeof window.top !== 'undefined') {
try {
// Test if we can access the top window
var topLocation = window.top.location.toString();
if (topLocation.indexOf(self.location.host) === -1) {
// Different domain - break out
window.top.location = self.location;
}
} catch (e) {
// Cross-origin iframe detected
window.location = 'about:blank';
}
}
}CSS can be used to make iframe embedding difficult by preventing transparency and hiding content when framed.
/* Prevent transparency when in iframe */
body {
background-color: white !important;
opacity: 1 !important;
}
/* Hide content when in iframe */
html:not(:root) body {
display: none !important;
}
/* Alternative: Show warning when framed */
html:not(:root) body::before {
content: "This page cannot be displayed in a frame.";
display: block;
text-align: center;
padding: 50px;
background: red;
color: white;
font-size: 24px;
}Require user confirmation for sensitive actions. This adds a layer of protection even if clickjacking bypasses other defenses.
function confirmAction(action, message) {
if (confirm(message)) {
// User confirmed - proceed with action
executeAction(action);
} else {
// User cancelled - abort action
return false;
}
}
// Usage for sensitive operations
confirmAction('transfer',
'Are you sure you want to transfer $1,000?');Adding CAPTCHA to sensitive actions can prevent automated clickjacking attacks.
Implement delays for critical actions to give users time to notice and cancel unintended operations.
function delayedAction(action, delay = 3000) {
const countdown = document.getElementById('countdown');
let timeLeft = delay / 1000;
const timer = setInterval(() => {
timeLeft--;
countdown.textContent = timeLeft;
if (timeLeft <= 0) {
clearInterval(timer);
executeAction(action);
}
}, 1000);
// Cancel button
document.getElementById('cancel-btn').onclick = () => {
clearInterval(timer);
showNotification('Action cancelled');
};
}Set cookies with SameSite attributes to prevent CSRF attacks that often accompany clickjacking attacks.
Set-Cookie: sessionid=abc123; SameSite=Strict; Secure; HttpOnlyImplement comprehensive CSP policies that restrict script execution and resource loading.
Content-Security-Policy: default-src 'self'; script-src 'self'; frame-ancestors 'none';Use SRI to ensure external resources haven't been tampered with.
<script src="app.js" integrity="sha384-..." crossorigin="anonymous"></script>Check security headers in browser dev tools:
Use specialized tools to test clickjacking protection:
Test your site by embedding it in an iframe:
<!DOCTYPE html>
<html>
<head>
<title>Clickjacking Test</title>
</head>
<body>
<h1>Testing iframe embedding</h1>
<iframe src="https://your-site.com"
width="800"
height="600"
frameborder="1">
</iframe>
</body>
</html>If your site doesn't load in the iframe, protection is working!
Use our free scanner to verify your clickjacking protection is working correctly
Maximum 60 characters