当前位置: 首页 > news >正文

点击劫持攻击完整防护指南

本文从小白角度全面讲解点击劫持攻击(Clickjacking)的原理、攻击方式、真实案例,以及详细的防护措施。涵盖 X-Frame-Options、Content Security Policy、JavaScript 防护等多种方法,并提供实际代码示例和最佳实践。

目录

  1. 什么是点击劫持攻击?
  2. 点击劫持是如何工作的?
  3. 为什么点击劫持很危险?
  4. 真实攻击案例
  5. 如何检测点击劫持攻击?
  6. 防护措施一:X-Frame-Options 响应头
  7. 防护措施二:Content Security Policy(CSP)
  8. 防护措施三:JavaScript 防护(帧破坏)
  9. 防护措施四:SameSite Cookie 属性
  10. 完整防护方案(推荐)
  11. 前端开发者的防护责任
  12. 常见问题与最佳实践

什么是点击劫持攻击?

简单理解

想象一下,你看到一个好看的按钮写着"点击领取奖品",你以为点击的是这个按钮,但实际上,你的点击被"偷偷"转发到了另一个隐藏在下面的按钮上。这就是点击劫持攻击(Clickjacking)

点击劫持,也被称为UI 覆盖攻击(UI Redressing Attack),是一种恶意网站将透明的 iframe 叠加在另一个网页上,诱骗用户点击他们以为是无害按钮的技术。

专业定义

点击劫持是一种视觉欺骗攻击,攻击者在一个网页上覆盖一个透明的 iframe,这个 iframe 加载了目标网站(通常是受信任的网站)。用户在点击看似正常的页面元素时,实际上点击的是隐藏在下面的目标网站的操作按钮。

可视化理解

┌─────────────────────────────────┐
│   恶意的诱骗页面(攻击者控制)      │
│                                 │
│   [看起来很正常的按钮]           │
│   "点击领取奖品"                 │
│                                 │
│   ╔═══════════════════════════╗ │
│   ║ 透明 iframe(看不见)       ║ │
│   ║ 加载了目标网站               ║ │
│   ║ 例如:银行转账确认按钮       ║ │
│   ╚═══════════════════════════╝ │
│                                 │
└─────────────────────────────────┘用户以为点击的是"领取奖品"
实际上点击的是"确认转账"按钮

点击劫持是如何工作的?

攻击流程步骤详解

第一步:创建恶意页面

攻击者创建一个看起来正常、吸引人的网页:

<!DOCTYPE html>
<html>
<head><title>免费领取大奖!</title><style>.fake-button {position: absolute;top: 200px;left: 200px;width: 200px;height: 50px;background: linear-gradient(45deg, #ff6b6b, #4ecdc4);color: white;border: none;border-radius: 10px;font-size: 18px;cursor: pointer;z-index: 2;}/* 透明 iframe,完全覆盖在按钮上方 */.hidden-iframe {position: absolute;top: 200px;left: 200px;width: 200px;height: 50px;opacity: 0;  /* 完全透明 */z-index: 3;  /* 在按钮上方 */pointer-events: all;}</style>
</head>
<body><h1>🎉 恭喜您!您获得了价值 10000 元的大奖!</h1><p>只需点击下方按钮即可领取:</p><!-- 假的按钮(诱饵) --><button class="fake-button">立即领取大奖</button><!-- 透明的 iframe,加载目标网站 --><iframe class="hidden-iframe"src="https://bank.example.com/transfer?to=hacker&amount=10000"></iframe><script>// 当用户点击时,iframe 会捕获这个点击事件// 如果目标网站没有防护,用户的点击会被转发到 iframe 内的按钮</script>
</body>
</html>
第二步:精确定位覆盖

攻击者使用 CSS 让 iframe 完全覆盖在诱骗按钮上:

/* 关键技术:绝对定位 + 透明度 + z-index */
.hidden-iframe {position: absolute;     /* 绝对定位 */opacity: 0;             /* 完全透明,用户看不见 */z-index: 9999;          /* 确保在最上层 */pointer-events: all;    /* 确保可以接收点击事件 */
}
第三步:用户被欺骗
  1. 用户访问恶意网页
  2. 看到诱人的"领取大奖"按钮
  3. 点击按钮
  4. 实际上点击的是透明的 iframe
  5. iframe 内的目标网站按钮被触发
  6. 攻击成功!

更复杂的攻击场景

场景一:社交媒体点赞劫持
<!-- 攻击者页面 -->
<div style="position: relative;"><h2>看这个有趣的视频!</h2><!-- 诱骗用户点击的区域 --><div class="fake-content"><img src="cute-cat.jpg" alt="可爱的猫咪"></div><!-- 透明 iframe 覆盖在图片上 --><iframe src="https://social-media.com/post/123/like"style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; z-index: 9999;"></iframe>
</div>

用户点击图片以为在看视频,实际触发了"点赞"操作。

场景二:银行转账劫持
<!-- 攻击者页面 -->
<div class="game-container"><h1>🐍 贪吃蛇小游戏</h1><p>点击开始游戏按钮:</p><button class="start-button">开始游戏</button><!-- 透明 iframe,加载银行转账页面 --><iframe src="https://bank.com/transfer?to=hacker_account&amount=5000"style="position: absolute; top: 150px; left: 50%; transform: translateX(-50%);width: 400px; height: 300px; opacity: 0; z-index: 9999;"></iframe>
</div><script>
// 更精确的控制:调整 iframe 位置,让它覆盖转账确认按钮
setTimeout(() => {const iframe = document.querySelector('iframe');// 动态调整位置,精确覆盖目标按钮iframe.style.left = '400px';iframe.style.top = '500px';
}, 1000);
</script>

为什么点击劫持很危险?

1. 用户完全不知情

用户认为自己点击的是正常的按钮,但实际上:

  • 可能触发了银行转账
  • 可能更改了账户设置
  • 可能发布了不当内容
  • 可能点赞/关注了不想要的账号
  • 可能授权了应用访问权限

2. 结合社会工程学

点击劫持经常与其他攻击结合:

用户收到邮件:
"您的账户异常,请立即点击这里验证"→ 跳转到恶意页面
→ 看起来像是银行的登录页面
→ 用户输入密码
→ 密码被窃取 + 同时触发账户设置更改(通过点击劫持)

3. 绕过传统安全检查

  • ✅ 目标网站有 HTTPS 加密
  • ✅ 用户已登录(有有效的 Cookie)
  • ✅ 有 CSRF Token 保护
  • ❌ 但没有防止在 iframe 中加载

结果:攻击者利用用户已建立的信任关系进行攻击。

4. 难以追溯

用户不知道自己被攻击了:

  • 没有明显的异常
  • 攻击发生在正常的网页交互中
  • 难以向用户解释发生了什么

真实攻击案例

案例一:Twitter 点击劫持(2009年)

攻击方式:

  • 攻击者创建了一个"TwitPorn"页面
  • 页面显示"点击按钮看图片"
  • 透明 iframe 加载了 Twitter 的关注按钮
  • 用户点击后,自动关注了攻击者的账号

影响:

  • 大量用户被诱骗关注恶意账号
  • Twitter 发现后紧急修复,推出了防护措施

案例二:Facebook Like 劫持(2010年)

攻击方式:

  • 恶意页面显示诱人的内容(如"点击看美女照片")
  • 透明 iframe 覆盖在内容上
  • iframe 加载了 Facebook 的 Like 按钮
  • 用户点击后,自动点赞了恶意页面

影响:

  • 恶意内容通过病毒式传播获得大量点赞
  • 用户的好友看到这些内容,形成链式反应

案例三:银行转账劫持(2013年)

攻击方式:

  • 钓鱼邮件声称"账户需要验证"
  • 点击链接进入"银行安全页面"
  • 页面看起来正常,让用户放心
  • 透明 iframe 覆盖在"继续"按钮上
  • iframe 中加载了真实的银行转账确认页面
  • 用户点击"继续",实际确认了一笔转账

影响:

  • 用户资金被盗
  • 银行声誉受损

案例四:Adobe Flash 点击劫持(2015年)

攻击方式:

  • 利用 Flash 的跨域特性
  • 创建透明 Flash 覆盖层
  • 即使用户禁止了 iframe,Flash 仍然可以工作
  • 劫持用户的点击操作

如何检测点击劫持攻击?

方法一:作为普通用户检测

1. 检查是否可以选中文字

在可疑页面上尝试选中文字:

  • 如果可以选中 → 可能是正常页面
  • 如果不能选中 → 可能有透明层覆盖
2. 检查鼠标悬停效果

将鼠标悬停在按钮上:

  • 查看浏览器状态栏显示的链接地址
  • 如果不一致,可能是攻击页面
3. 使用浏览器开发者工具
按 F12 打开开发者工具
→ 点击 Elements 标签
→ 查看是否有多个重叠的元素
→ 检查 iframe 标签
4. 检查页面源代码

右键 → 查看页面源代码:

  • 搜索 <iframe> 标签
  • 如果发现可疑的透明 iframe,可能是攻击

方法二:作为开发者检测自己的网站

1. 检查响应头
# 使用 curl 检查响应头
curl -I https://your-website.com# 查看是否有 X-Frame-Options 或 CSP 头部

没有防护的网站:

HTTP/1.1 200 OK
Content-Type: text/html
...

有防护的网站:

HTTP/1.1 200 OK
Content-Type: text/html
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
...
2. 尝试在自己的页面中嵌入 iframe

创建一个测试页面:

<!DOCTYPE html>
<html>
<head><title>点击劫持测试</title>
</head>
<body><h1>测试:尝试将目标网站嵌入 iframe</h1><!-- 如果可以嵌入,说明没有防护 --><iframe src="https://your-website.com" width="100%" height="600px"></iframe><p>如果你能看到上面的内容,说明目标网站没有防护点击劫持。</p>
</body>
</html>
  • 如果可以正常显示 → ❌ 没有防护
  • 如果显示空白或被阻止 → ✅ 有防护
3. 使用在线检测工具
  • Clickjacking Test Tool
  • Security Headers

防护措施一:X-Frame-Options 响应头

什么是 X-Frame-Options?

X-Frame-Options 是 HTTP 响应头,告诉浏览器是否允许将页面嵌入到 <iframe><frame><embed><object> 中。

三种取值

1. DENY - 完全禁止
X-Frame-Options: DENY

效果:

  • 页面不能在任何情况下被嵌入 iframe
  • 最安全,但也最严格
  • 即使用户想在自己的网站中嵌入也不可以

适用场景:

  • 银行网站
  • 支付页面
  • 敏感操作页面

示例代码(Node.js/Express):

app.use((req, res, next) => {res.setHeader('X-Frame-Options', 'DENY');next();
});
2. SAMEORIGIN - 同源允许
X-Frame-Options: SAMEORIGIN

效果:

  • 只允许同源(同一个域名)的页面嵌入
  • 其他域名的页面不能嵌入
  • 平衡了安全性和灵活性

适用场景:

  • 大多数网站
  • 需要在自己网站内嵌页面的场景
  • 推荐使用

示例代码(Node.js/Express):

app.use((req, res, next) => {res.setHeader('X-Frame-Options', 'SAMEORIGIN');next();
});
3. ALLOW-FROM uri - 指定来源允许

⚠️ 注意:此选项已被废弃,不推荐使用

旧版浏览器支持,但现代浏览器(Chrome、Firefox)已不支持。

完整的服务器配置示例

Node.js/Express
const express = require('express');
const app = express();// 全局设置 X-Frame-Options
app.use((req, res, next) => {// 对于敏感页面,使用 DENYif (req.path.startsWith('/payment') || req.path.startsWith('/transfer')) {res.setHeader('X-Frame-Options', 'DENY');} // 对于普通页面,使用 SAMEORIGINelse {res.setHeader('X-Frame-Options', 'SAMEORIGIN');}next();
});app.listen(3000);
Nginx
# 全局设置
add_header X-Frame-Options "SAMEORIGIN" always;# 或者针对特定路径
location /payment {add_header X-Frame-Options "DENY" always;
}
Apache
# .htaccess 文件
Header always set X-Frame-Options "SAMEORIGIN"# 或者针对特定路径
<LocationMatch "^/payment">Header always set X-Frame-Options "DENY"
</LocationMatch>
PHP
<?php
// 在 PHP 文件中设置
header('X-Frame-Options: SAMEORIGIN');// 或者针对不同页面
if (strpos($_SERVER['REQUEST_URI'], '/payment') !== false) {header('X-Frame-Options: DENY');
} else {header('X-Frame-Options: SAMEORIGIN');
}
?>

验证是否生效

// 在前端测试
fetch('https://your-website.com', { method: 'HEAD' }).then(response => {console.log('X-Frame-Options:', response.headers.get('X-Frame-Options'));});
# 使用 curl 验证
curl -I https://your-website.com | grep -i "x-frame-options"

防护措施二:Content Security Policy(CSP)

什么是 CSP?

Content Security Policy(内容安全策略)是一个强大的安全机制,不仅可以防护点击劫持,还可以防护 XSS、数据注入等多种攻击。

CSP frame-ancestors 指令

专门用于防护点击劫持的 CSP 指令是 frame-ancestors

frame-ancestors 的值

1. frame-ancestors 'none' - 完全禁止
Content-Security-Policy: frame-ancestors 'none'

等同于 X-Frame-Options: DENY

示例:

app.use((req, res, next) => {res.setHeader("Content-Security-Policy", "frame-ancestors 'none'");next();
});
2. frame-ancestors 'self' - 同源允许
Content-Security-Policy: frame-ancestors 'self'

等同于 X-Frame-Options: SAMEORIGIN

示例:

app.use((req, res, next) => {res.setHeader("Content-Security-Policy", "frame-ancestors 'self'");next();
});
3. frame-ancestors https://example.com - 指定域名
Content-Security-Policy: frame-ancestors 'self' https://trusted-site.com

优势:

  • 可以指定多个允许的域名
  • X-Frame-Options 更灵活

示例:

app.use((req, res, next) => {// 允许同源和特定域名嵌入res.setHeader("Content-Security-Policy", "frame-ancestors 'self' https://dashboard.example.com https://admin.example.com");next();
});

完整的 CSP 配置示例

CSP 不仅可以防护点击劫持,还可以防护其他攻击:

app.use((req, res, next) => {const cspHeader = ["default-src 'self'",                    // 默认只允许同源资源"script-src 'self' 'unsafe-inline'",     // 允许同源脚本和内联脚本"style-src 'self' 'unsafe-inline'",      // 允许同源样式和内联样式"img-src 'self' data: https:",           // 允许同源图片、data URI 和 HTTPS 图片"font-src 'self'",                       // 只允许同源字体"frame-ancestors 'self'",                // 防护点击劫持:只允许同源嵌入"base-uri 'self'",                       // 只允许同源的 base URI].join('; ');res.setHeader("Content-Security-Policy", cspHeader);next();
});

X-Frame-Options vs CSP frame-ancestors

特性X-Frame-OptionsCSP frame-ancestors
浏览器支持所有现代浏览器所有现代浏览器
灵活性较简单更灵活(可指定多个域名)
功能范围只防护点击劫持可防护多种攻击
推荐度基础防护推荐使用(更现代)

最佳实践:同时使用两者

app.use((req, res, next) => {// CSP(现代标准,推荐)res.setHeader("Content-Security-Policy", "frame-ancestors 'self'");// X-Frame-Options(向后兼容旧浏览器)res.setHeader("X-Frame-Options", "SAMEORIGIN");next();
});

为什么同时使用?

  • CSP frame-ancestors 是现代标准,功能更强大
  • X-Frame-Options 确保旧浏览器也能得到保护

防护措施三:JavaScript 防护(帧破坏)

什么是帧破坏(Frame Busting)?

帧破坏是一种客户端 JavaScript 技术,检测页面是否被嵌入到 iframe 中,如果是,则尝试"破坏"框架(跳转到顶层窗口)。

基础帧破坏代码

// 方法一:检查 window.top 和 window.self
if (window.top !== window.self) {// 页面被嵌入到 iframe 中window.top.location = window.self.location;// 强制跳转到当前页面(打破框架)
}

更强大的帧破坏代码

完整版本(推荐)
(function() {'use strict';// 检查是否被嵌入if (window.top !== window.self) {// 尝试打破框架try {// 方法1:直接跳转window.top.location = window.self.location;} catch (e) {// 方法2:如果被阻止,创建覆盖整个页面的警告document.body.innerHTML = '';const warning = document.createElement('div');warning.style.cssText = `position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: #ff4444;color: white;display: flex;align-items: center;justify-content: center;font-size: 24px;z-index: 999999;text-align: center;padding: 20px;box-sizing: border-box;`;warning.innerHTML = `<div><h1>⚠️ 安全警告</h1><p>此页面不能嵌入到其他网页中。</p><p>请在新窗口中打开:</p><a href="${window.location.href}" style="color: white; text-decoration: underline; font-size: 20px;"target="_blank">点击这里打开</a></div>`;document.body.appendChild(warning);}}
})();
增强版本(支持更多场景)
(function() {'use strict';// 多重检测const isFramed = window.top !== window.self || window.frameElement !== null ||window.parent !== window.self;if (isFramed) {// 尝试打破框架try {// 直接跳转(最直接的方法)window.top.location = window.self.location;} catch (e1) {try {// 使用 parentwindow.parent.location = window.self.location;} catch (e2) {// 如果都被阻止,显示警告页面document.documentElement.innerHTML = `<!DOCTYPE html><html><head><title>安全警告</title><meta charset="utf-8"><style>body {margin: 0;padding: 0;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;display: flex;align-items: center;justify-content: center;min-height: 100vh;color: white;}.container {text-align: center;padding: 40px;background: rgba(255, 255, 255, 0.1);border-radius: 20px;backdrop-filter: blur(10px);max-width: 600px;}h1 { font-size: 48px; margin: 0 0 20px 0; }p { font-size: 18px; line-height: 1.6; margin: 20px 0; }a {display: inline-block;margin-top: 20px;padding: 15px 30px;background: white;color: #667eea;text-decoration: none;border-radius: 10px;font-weight: bold;font-size: 16px;transition: transform 0.2s;}a:hover { transform: scale(1.05); }</style></head><body><div class="container"><h1>⚠️</h1><h2>安全警告</h2><p>此页面出于安全考虑,不能嵌入到其他网页中。</p><p>请在浏览器的新标签页中打开此页面。</p><a href="${window.location.href}" target="_blank">在新窗口中打开</a></div></body></html>`;}}}
})();

在 HTML 中使用

方式一:内联脚本(快速生效)
<!DOCTYPE html>
<html>
<head><title>受保护的页面</title><script>// 立即执行,在页面加载前就检测if (window.top !== window.self) {window.top.location = window.self.location;}</script>
</head>
<body><!-- 页面内容 -->
</body>
</html>
方式二:外部脚本
<!DOCTYPE html>
<html>
<head><title>受保护的页面</title><!-- 在 head 中立即加载 --><script src="/js/frame-busting.js"></script>
</head>
<body><!-- 页面内容 -->
</body>
</html>

帧破坏的局限性

⚠️ 重要:帧破坏不是完美的防护措施

1. JavaScript 可以被禁用

如果用户禁用了 JavaScript,帧破坏代码无法运行。

2. 可以被绕过

攻击者可以使用 sandbox 属性:

<!-- 攻击者的页面 -->
<iframe src="https://target-site.com"sandbox="allow-scripts allow-forms allow-same-origin"onload="this.contentWindow.frameElement = null;">
</iframe>

或者使用 CSS 覆盖:

/* 攻击者可以阻止显示警告 */
iframe {pointer-events: none;
}
3. 不应该单独依赖

帧破坏应该作为补充措施,而不是主要防护手段。

最佳实践

推荐做法:

  1. 主要防护:使用 X-Frame-Options 或 CSP frame-ancestors(服务器端)
  2. 补充防护:使用 JavaScript 帧破坏(客户端)
  3. 深度防御:多层防护措施

防护措施四:SameSite Cookie 属性

什么是 SameSite?

SameSite 是 Cookie 的一个属性,用于防止跨站请求伪造(CSRF)攻击,同时也能在一定程度上帮助防护点击劫持。

SameSite 的值

1. SameSite=Strict - 严格模式
// 设置 Cookie
Set-Cookie: sessionId=abc123; SameSite=Strict; Secure; HttpOnly

效果:

  • Cookie 只在同站请求中发送
  • 完全禁止跨站发送 Cookie
  • 即使点击链接跳转也不会发送 Cookie

适用场景:

  • 银行网站
  • 高安全要求的应用
2. SameSite=Lax - 宽松模式(推荐)
Set-Cookie: sessionId=abc123; SameSite=Lax; Secure; HttpOnly

效果:

  • GET 请求允许跨站发送 Cookie(例如从搜索引擎点击链接)
  • POST 请求禁止跨站发送 Cookie
  • 在 iframe 中加载页面时,不发送 Cookie

适用场景:

  • 大多数网站(推荐使用)
  • 需要 SEO 友好的网站
3. SameSite=None - 无限制
Set-Cookie: sessionId=abc123; SameSite=None; Secure; HttpOnly

效果:

  • 允许跨站发送 Cookie
  • ⚠️ 必须配合 Secure 使用(HTTPS)

适用场景:

  • 需要在第三方网站嵌入的页面
  • 单点登录(SSO)场景

SameSite 如何帮助防护点击劫持?

当页面被嵌入到跨站 iframe 中时:

  • 如果 Cookie 设置了 SameSite=LaxStrict
  • Cookie 不会被发送到服务器
  • 即使用户已登录,iframe 中的页面也无法获取登录状态
  • 攻击者无法利用用户的登录状态进行恶意操作

代码示例

Node.js/Express
const express = require('express');
const session = require('express-session');
const app = express();app.use(session({secret: 'your-secret-key',resave: false,saveUninitialized: false,cookie: {secure: true,        // 只在 HTTPS 下发送httpOnly: true,      // 防止 XSS 攻击sameSite: 'lax',     // 防护点击劫持和 CSRFmaxAge: 24 * 60 * 60 * 1000  // 24 小时}
}));
PHP
<?php
// 设置 Cookie
setcookie('sessionId','abc123',['secure' => true,      // 只在 HTTPS 下发送'httponly' => true,    // 防止 XSS'samesite' => 'Lax'    // 防护点击劫持]
);
?>
直接设置 HTTP 响应头
app.use((req, res, next) => {// 设置 Cookie(示例)res.cookie('sessionId', 'abc123', {secure: true,httpOnly: true,sameSite: 'lax'});next();
});

SameSite 的浏览器支持

  • Chrome 51+
  • Firefox 60+
  • Safari 12+
  • Edge 79+

所有现代浏览器都支持 SameSite 属性。


完整防护方案(推荐)

多层次防护策略

单一防护措施可能被绕过,建议使用**深度防御(Defense in Depth)**策略。

推荐配置(生产环境)

1. 服务器端响应头配置
// Node.js/Express 完整示例
const express = require('express');
const helmet = require('helmet');  // 推荐使用 helmet
const app = express();// 使用 helmet 自动设置安全响应头
app.use(helmet({frameguard: { action: 'sameorigin' },  // X-Frame-Options: SAMEORIGINcontentSecurityPolicy: {directives: {defaultSrc: ["'self'"],styleSrc: ["'self'", "'unsafe-inline'"],scriptSrc: ["'self'"],imgSrc: ["'self'", "data:", "https:"],frameAncestors: ["'self'"],  // 防护点击劫持// ... 其他 CSP 指令},},
}));// 或者手动设置
app.use((req, res, next) => {// 主要防护:CSP frame-ancestorsres.setHeader("Content-Security-Policy","frame-ancestors 'self'; default-src 'self';");// 向后兼容:X-Frame-Optionsres.setHeader("X-Frame-Options", "SAMEORIGIN");// Cookie 防护res.cookie('sessionId', req.sessionID, {secure: true,httpOnly: true,sameSite: 'lax'});next();
});app.listen(3000);
2. 前端 JavaScript 帧破坏

在 HTML 的 <head> 中立即执行:

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>受保护的页面</title><!-- 帧破坏脚本(立即执行) --><script>(function() {if (window.top !== window.self) {try {window.top.location = window.self.location;} catch (e) {document.body.innerHTML = '<div style="text-align:center;padding:50px;"><h1>⚠️ 安全警告</h1><p>此页面不能嵌入到其他网页中。</p><a href="' + window.location.href + '" target="_blank">在新窗口中打开</a></div>';}}})();</script>
</head>
<body><!-- 页面内容 -->
</body>
</html>
3. Nginx 完整配置示例
server {listen 443 ssl http2;server_name example.com;# SSL 配置...# 安全响应头add_header X-Frame-Options "SAMEORIGIN" always;add_header Content-Security-Policy "frame-ancestors 'self'; default-src 'self';" always;add_header X-Content-Type-Options "nosniff" always;add_header X-XSS-Protection "1; mode=block" always;# Cookie 设置(在应用层设置)location / {proxy_pass http://backend;# ... 其他代理配置}# 敏感页面更严格的防护location ~ ^/(payment|transfer|admin) {add_header X-Frame-Options "DENY" always;add_header Content-Security-Policy "frame-ancestors 'none';" always;proxy_pass http://backend;}
}

不同场景的防护策略

场景一:公开内容网站(如博客)
// 允许嵌入,但需要验证来源
res.setHeader("Content-Security-Policy", "frame-ancestors 'self' https://trusted-partner.com");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
场景二:银行/支付网站
// 完全禁止嵌入
res.setHeader("Content-Security-Policy", "frame-ancestors 'none'");
res.setHeader("X-Frame-Options", "DENY");// 加上 JavaScript 帧破坏
// 加上 SameSite=Lax Cookie
场景三:需要嵌入的页面(如视频播放器)
// 允许嵌入,但设置 SameSite=None Cookie(需要 HTTPS)
res.setHeader("Content-Security-Policy", "frame-ancestors *");
res.cookie('sessionId', value, { sameSite: 'none', secure: true });

验证防护是否生效

测试脚本
<!-- test-clickjacking.html -->
<!DOCTYPE html>
<html>
<head><title>点击劫持测试</title>
</head>
<body><h1>测试:尝试嵌入目标网站</h1><iframe src="https://your-website.com" width="100%" height="600px"id="test-frame"></iframe><div id="result"></div><script>const iframe = document.getElementById('test-frame');const result = document.getElementById('result');iframe.onload = function() {try {// 尝试访问 iframe 内容const iframeContent = iframe.contentDocument || iframe.contentWindow.document;if (iframeContent && iframeContent.body) {result.innerHTML = '<p style="color: red;">❌ 防护失败:页面可以被嵌入</p>';}} catch (e) {result.innerHTML = '<p style="color: green;">✅ 防护成功:页面被阻止嵌入(' + e.message + ')</p>';}};setTimeout(() => {if (iframe.contentDocument === null && iframe.contentWindow.document === null) {result.innerHTML = '<p style="color: green;">✅ 防护成功:无法访问 iframe 内容</p>';}}, 2000);</script>
</body>
</html>
使用命令行工具
# 检查响应头
curl -I https://your-website.com | grep -E "(X-Frame-Options|Content-Security-Policy)"# 预期输出:
# X-Frame-Options: SAMEORIGIN
# Content-Security-Policy: frame-ancestors 'self';

前端开发者的防护责任

开发时需要注意的事项

1. 不要轻易使用 iframe
// ❌ 不好的做法:直接嵌入第三方页面
<iframe src="https://bank.com/login"></iframe>// ✅ 好的做法:在新窗口打开
<a href="https://bank.com/login" target="_blank">登录</a>
2. 如果必须使用 iframe,验证来源
// 只嵌入可信任的来源
const trustedDomains = ['https://trusted-partner.com'];function isValidSource(url) {return trustedDomains.some(domain => url.startsWith(domain));
}if (isValidSource(iframeSrc)) {// 允许嵌入
} else {// 拒绝嵌入
}
3. 检查自己网站的响应头
// 在开发环境定期检查
fetch('/api/check-headers').then(res => {const frameOptions = res.headers.get('X-Frame-Options');const csp = res.headers.get('Content-Security-Policy');if (!frameOptions && !csp?.includes('frame-ancestors')) {console.warn('⚠️ 警告:未检测到点击劫持防护!');}});
4. 敏感操作页面加强防护
// 在支付页面、设置页面等敏感页面
if (window.location.pathname.includes('/payment')) {// 双重检查:服务器端 + 客户端if (window.top !== window.self) {window.top.location = window.self.location;}
}

使用安全库

推荐:helmet.js(Node.js)
npm install helmet
const helmet = require('helmet');
app.use(helmet({frameguard: { action: 'sameorigin' },contentSecurityPolicy: {directives: {frameAncestors: ["'self'"],},},
}));

常见问题与最佳实践

常见问题

Q1: X-Frame-Options 和 CSP frame-ancestors 哪个更好?

A: CSP frame-ancestors 是更现代的标准,功能更强大(可以指定多个域名)。但建议同时使用两者:

  • CSP frame-ancestors:现代浏览器
  • X-Frame-Options:向后兼容旧浏览器
Q2: 如果我的网站需要被嵌入到其他网站怎么办?

A: 使用 CSP 指定允许的域名:

res.setHeader("Content-Security-Policy","frame-ancestors 'self' https://trusted-partner1.com https://trusted-partner2.com"
);

⚠️ 注意: 只允许你完全信任的域名。

Q3: JavaScript 帧破坏够用吗?

A: 不够。JavaScript 可以被禁用或绕过。帧破坏只能作为补充措施,主要防护应该依赖服务器端的响应头。

Q4: SameSite Cookie 会影响用户体验吗?

A: SameSite=Lax 对用户体验影响很小:

  • 正常的页面跳转不受影响
  • 从搜索引擎点击链接可以正常登录
  • 只有在 iframe 中加载时才不发送 Cookie(这正是我们想要的安全行为)
Q5: 如何测试我的防护是否生效?

A:

  1. 创建一个测试页面,尝试用 iframe 嵌入你的网站
  2. 检查响应头是否包含 X-Frame-Options 或 CSP frame-ancestors
  3. 使用在线工具:https://securityheaders.com/
Q6: 点击劫持只影响网站吗?

A: 主要影响 Web 应用,但移动应用(WebView)也可能受到影响。

最佳实践总结

✅ 推荐做法
  1. 多层防护

    • 服务器端:X-Frame-Options + CSP frame-ancestors
    • 客户端:JavaScript 帧破坏(补充)
    • Cookie:SameSite=Lax
  2. 敏感页面加强防护

    • 支付页面:DENY'none'
    • 普通页面:SAMEORIGIN'self'
  3. 定期检查

    • 使用工具检查响应头
    • 测试是否可以嵌入 iframe
  4. 保持更新

    • 关注安全公告
    • 及时更新防护措施
❌ 避免的做法
  1. 不要单独依赖 JavaScript 防护

    • JavaScript 可以被禁用
  2. 不要完全禁止嵌入(如果确实需要)

    • 使用 CSP 指定允许的域名
  3. 不要忽略旧浏览器

    • 同时设置 X-Frame-Options 和 CSP
  4. 不要在生产环境关闭防护进行测试

    • 即使短暂关闭也可能被利用

总结

点击劫持是一种危险的攻击方式,但通过正确的防护措施可以有效防范:

核心防护措施

  1. X-Frame-Options 响应头:基础防护,向后兼容
  2. CSP frame-ancestors:现代标准,功能强大
  3. JavaScript 帧破坏:补充防护,深度防御
  4. SameSite Cookie:防止利用登录状态

关键要点

  • 多层防护:不要依赖单一措施
  • 服务器端优先:主要防护在服务器端
  • 敏感页面加强:支付等页面使用最严格的防护
  • 定期检查:使用工具验证防护是否生效

行动起来

立即检查你的网站:

  1. 检查响应头是否有防护措施
  2. 测试是否可以嵌入 iframe
  3. 根据需求选择适合的防护策略
  4. 实施并验证防护效果

保护用户就是保护你的业务!🛡️


参考资料

  • OWASP Clickjacking Defense Cheat Sheet
  • MDN: X-Frame-Options
  • MDN: Content-Security-Policy
  • MDN: SameSite Cookies
http://www.dtcms.com/a/561459.html

相关文章:

  • 预备知识总结
  • 想自己做网站流程抚顺市营商环境建设局网站
  • 西安网站关键词优化费用淄博网站制作服务优化
  • alsa之ASOC架构学习
  • 技术深度解析:优秘智能企业智脑5.1.0版本 AI大管家架构设计与实现原理
  • 小型电商网站开发2022世界500强企业排名
  • 免费行情网站大全公司网站域名申请流程
  • PYNZ搭建高性能CNN部署的快速 FPGA 原型验证框架
  • Arduino硬件原理3:核心单片机
  • 学习周报二十
  • 建筑培训网站有哪些哪里有网站建设项目
  • 哪里可以捡到玉石谷歌seo零基础教程
  • 5G-NR标准的QC-LDPC码
  • Google NotebookLM重磅升级:从摘要工具到智能研究引擎的范式转移
  • C++11 面试题插入(左值引用和右值引用的区别)移动构造和移动赋值C++新标准如何解决传值返回但对象销毁问题
  • 住房城乡与建设厅网站首页智慧团建团员注册入口
  • 网站地图可以自己做么杭州网站制作 乐云践新
  • Qt5设定编译生成程序的路径和文件名
  • SpringBoot18-文件上传助手MultipartResolver
  • python学习之os,sys,time,random模块
  • 排序算法实战:从插入排序到希尔排序的实现与性能对决
  • Harmony鸿蒙开发0基础入门到精通Day10--JavaScript篇
  • VMware安装CentOS7操作系统
  • 搬瓦工做网站方法wordpress数据类型
  • 常德网站网站建设软件工程师英文
  • Win11超精简定制版本Tiny11安装教程来袭
  • 【第1章>第2节】图像“腐蚀”处理的理论分析与MATLAB仿真测试
  • 如何将BOOST库集成到VS2019中去使用呢?
  • 黑龙江做网站公司网站建设方案书网络部署方案
  • 乐清微网站建设做网络运营需要掌握什么