Clickjacking is when an attacker loads your site inside an invisible frame on their own page and tricks a logged-in user into clicking something they cannot see, approving an action, changing a setting, confirming a payment. The defense is to tell browsers your site may not be framed by other origins, with X-Frame-Options or a Content-Security-Policy frame-ancestors directive.
Why it's a problem
Because the framed page is your real, authenticated site, the clicks land as genuine user actions. The attacker overlays their own bait over your buttons, so the user thinks they are clicking one thing while actually clicking another on your site. Any state-changing action, from settings to transfers, can be hijacked this way.
The pattern
// no framing protection — any site can embed yours
app.get("/", (req, res) => res.send(html));The fix
// block framing by other origins
app.use((req, res, next) => {
res.setHeader("X-Frame-Options", "DENY");
res.setHeader(
"Content-Security-Policy",
"frame-ancestors 'none'"
);
next();
});Why AI tools write this
Framing protection is a response header nothing in a feature prompt asks for, and the app works identically with or without it. So generated apps ship without it, leaving the clickjacking surface open by default.
The quick fix
- Set X-Frame-Options: DENY (or SAMEORIGIN if you frame your own pages).
- Add a Content-Security-Policy with frame-ancestors 'none' as the modern equivalent.
- Apply these headers globally, at the framework or CDN level.