Clickjacking remains a stealthy but potent web security threat. This guide provides robust techniques and configuration examples to help sysadmins harden their systems against UI redress attacks.
Clickjacking (User Interface redress attack) tricks users into clicking invisible or misleading elements embedded via iframes on a malicious site. In enterprise environments, it can lead to unauthorized operations, credential leaks, or administrative actions being hijacked.
How to Check if Your Site is Vulnerable
A site is vulnerable if it allows itself to be embedded via <iframe>
by external domains. To check, use:
curl -I https://yourdomain.com
If no X-Frame-Options
or Content-Security-Policy: frame-ancestors
header appears, the site is potentially at risk.
👉 Visit clickjacker.io to test if your site is vulnerable.
Key Defense Strategies
1. Set Protective HTTP Headers
a) X-Frame-Options (Legacy but still useful)
DENY
: Blocks all iframe embedding.SAMEORIGIN
: Allows embedding only from the same origin.ALLOW-FROM uri
: Deprecated and unsupported by modern browsers.
b) Content-Security-Policy (CSP) — frame-ancestors
A more modern, flexible and widely supported solution:
Content-Security-Policy: frame-ancestors 'none'
Options:
'none'
: No framing allowed at all.'self'
: Only same-origin embedding allowed.https://example.com
: Allows embedding only from a specific domain.
Server Config Examples
Apache
- Enable headers module:
sudo a2enmod headers && sudo systemctl restart apache2
- Edit VirtualHost config:
sudo nano /etc/apache2/sites-available/000-default.conf
- Add inside
<VirtualHost>
block:
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'none'"
- Restart Apache:
sudo systemctl restart apache2
Nginx
- Edit server block:
sudo nano /etc/nginx/sites-available/default
- Add:
add_header X-Frame-Options "DENY";
add_header Content-Security-Policy "frame-ancestors 'none'";
- Reload Nginx:
sudo systemctl reload nginx
WordPress
Edit wp-config.php
and append:
header('X-Frame-Options: SAMEORIGIN');
header("Content-Security-Policy: frame-ancestors 'none'");
Note: Some themes/plugins may override these headers. Validate with browser dev tools or CLI tools.
2. JavaScript Frame-Busting (Optional Layer)
Not as reliable as HTTP headers but useful as a fallback:
<style>html { display: none; }</style>
<script>
if (self === top) {
document.documentElement.style.display = 'block';
} else {
top.location = self.location;
}
</script>
Backend Language Examples
Node.js / Express.js
app.use((req, res, next) => {
res.setHeader("X-Frame-Options", "DENY");
res.setHeader("Content-Security-Policy", "frame-ancestors 'none'");
next();
});
Java (Servlet)
response.setHeader("X-Frame-Options", "DENY");
response.setHeader("Content-Security-Policy", "frame-ancestors 'none'");
PHP
header('X-Frame-Options: DENY');
header("Content-Security-Policy: frame-ancestors 'none'");
Verification & Testing
After deploying protections:
- Use SecurityHeaders.com for quick analysis.
- Manually check via:
curl -I https://yoursite.com
- Use OWASP ZAP or Burp Suite to simulate iframe attacks.
Final Thoughts
Clickjacking is easy to overlook but potentially devastating. In the era of SSO, cloud dashboards, and web-based admin panels, properly configured headers like Content-Security-Policy
and X-Frame-Options
are essential.
For defense-in-depth:
- Use CSP with
frame-ancestors
. - Optionally add JavaScript frame-busting.
- Monitor headers regularly as part of your security audits.
👉 Visit clickjacker.io to test if your site is vulnerable.