Clickjacking Prevention: Protecting Your WordPress Site
Learn how to prevent clickjacking attacks on WordPress using frame-busting techniques and security headers.
Clickjacking tricks users into clicking hidden elements by overlaying your site in an invisible frame. This attack can lead to unauthorized actions, data theft, and account compromise.
Understanding Clickjacking
How Clickjacking Works
- Attacker creates malicious page
- Your WordPress site loaded in invisible iframe
- Attacker positions elements over your site
- User thinks they click attacker's button
- Actually clicks hidden element on your site
Attack Scenario
<!-- Attacker's page -->
<style>
iframe {
position: absolute;
opacity: 0;
z-index: 2;
}
.decoy-button {
position: absolute;
z-index: 1;
}
</style>
<button class="decoy-button">Win a Prize!</button>
<iframe src="https://yoursite.com/wp-admin/options.php">
</iframe>
X-Frame-Options Header
Header Values
- DENY - Never allow framing
- SAMEORIGIN - Allow same-origin frames only
- ALLOW-FROM uri - Allow specific origin (deprecated)
WordPress Implementation
// Add X-Frame-Options header
add_action('send_headers', 'wpfs_frame_options');
function wpfs_frame_options() {
header('X-Frame-Options: SAMEORIGIN');
}
// WordPress admin already sends this header
// But verify it's not being overridden
htaccess Method
# Add to .htaccess
Header always set X-Frame-Options "SAMEORIGIN"
# Or completely deny framing
Header always set X-Frame-Options "DENY"
Content Security Policy
frame-ancestors Directive
Modern replacement for X-Frame-Options with more flexibility.
// CSP frame-ancestors
add_action('send_headers', 'wpfs_csp_frames');
function wpfs_csp_frames() {
header("Content-Security-Policy: frame-ancestors 'self'");
}
// Allow specific domains
header("Content-Security-Policy: frame-ancestors 'self' https://trusted.com");
JavaScript Frame Busting
Basic Frame Busting
<script>
if (top !== self) {
top.location = self.location;
}
</script>
Robust Frame Busting
<style id="antiClickjack">
body { display: none !important; }
</style>
<script>
if (self === top) {
var style = document.getElementById('antiClickjack');
style.parentNode.removeChild(style);
} else {
top.location = self.location;
}
</script>
WordPress-Specific Protections
Admin Area Protection
// Extra protection for admin
add_action('admin_init', 'wpfs_admin_frame_check');
function wpfs_admin_frame_check() {
if (!defined('IFRAME_REQUEST') || !IFRAME_REQUEST) {
header('X-Frame-Options: DENY');
}
}
Login Page Protection
// Protect login specifically
add_action('login_init', 'wpfs_login_frame_protection');
function wpfs_login_frame_protection() {
header('X-Frame-Options: DENY');
header("Content-Security-Policy: frame-ancestors 'none'");
}
Testing Clickjacking Protection
Manual Test
<!-- Test page -->
<iframe src="https://yoursite.com/wp-admin/"
width="800" height="600"></iframe>
<!-- Should show error or be blank if protected -->
Browser Developer Tools
- Check Response Headers tab
- Verify X-Frame-Options present
- Check CSP frame-ancestors
When to Allow Framing
Legitimate Iframe Uses
- Embedded content widgets
- Payment processor iframes
- Preview functionality
Selective Protection
// Allow specific pages to be framed
add_action('send_headers', 'wpfs_selective_framing');
function wpfs_selective_framing() {
global $post;
$allowed = array('embed-widget', 'preview');
if (!is_page($allowed)) {
header('X-Frame-Options: SAMEORIGIN');
}
}
Conclusion
Clickjacking prevention requires both HTTP headers and JavaScript defenses. Use X-Frame-Options and CSP frame-ancestors together for comprehensive protection against UI redressing attacks.
Written by Sarah Chen
WP Folder Shield Team