Improper Restriction of Rendered UI Layers or Frames (Clickjacking)
Clickjacking is a deceptive technique where an attacker embeds your website in a hidden frame on a malicious page, then overlays fake buttons or content on…
Clickjacking is a deceptive technique where an attacker embeds your website in a hidden frame on a malicious page, then overlays fake buttons or content on top. When users click what they think is a legitimate button, they're actually clicking something on your site — often performing sensitive actions like changing passwords, transferring funds, or granting permissions without realizing it. This weakness exists when a site doesn't tell browsers whether it's safe to be framed by other domains.
02How It Happens
A web application becomes vulnerable to clickjacking when it fails to set HTTP headers or frame-busting JavaScript that restrict how and where the page can be embedded. Browsers have no built-in protection against framing by default — it's the responsibility of each site to opt out. An attacker can then create a page that loads your site in an invisible <iframe>, position it precisely behind transparent overlays, and use CSS tricks to make their fake interface visible while your real interface remains hidden but clickable. The user's browser, following normal security rules, allows the click to reach your site because the user is technically interacting with content from your domain — they just can't see it.
03Real-World Impact
Clickjacking can lead to unauthorized account actions: a user might unknowingly grant browser permissions (camera, microphone, location), change account settings, approve financial transactions, or delete data. In high-stakes contexts like banking or email, a single click can compromise an entire account. The attack is particularly dangerous because it requires no malware, no phishing email, and no technical sophistication from the victim — just a visit to an attacker's page.
04Vulnerable & Fixed Patterns
Vulnerable pattern
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/settings')
def settings():
# No frame-restriction headers set
return render_template('settings.html')
if __name__ == '__main__':
app.run()
Why it's vulnerable: The application does not set X-Frame-Options or Content-Security-Policy headers, so any external site can embed this page in an iframe and overlay deceptive content on top of it.
Why it's vulnerable: The page sends no X-Frame-Options or Content-Security-Policy header, allowing it to be embedded in an iframe on any external domain.
Set X-Frame-Options: SAMEORIGIN on all pages to allow framing only by your own domain, or DENY to block framing entirely.
Use Content-Security-Policy: frame-ancestors 'self' as a modern, more flexible alternative (or in addition to X-Frame-Options).
Apply these headers globally via your web server configuration (nginx, Apache) or application middleware, not page-by-page.
Test that your site cannot be embedded in an iframe on an external domain — use browser developer tools or a simple test page.
For sensitive actions (password changes, fund transfers, permission grants), consider additional protections like CSRF tokens and user confirmation dialogs.
06Signs You May Already Be Affected
Look for unexpected user reports of unintended account changes or permission grants they don't recall authorizing. Check your web server logs for requests to sensitive endpoints (password changes, settings updates) that lack a clear user-initiated referrer or come from unusual patterns. If you can embed your site in an iframe on a test page without errors, your frame-restriction headers are not set.