hunt-clickjacking
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhat is Clickjacking
什么是 Clickjacking
Clickjacking (UI Redressing) lets an attacker load a target page inside a transparent iframe on a malicious site. The victim sees the attacker's decoy UI but clicks the hidden target UI beneath it. No JavaScript on the target is required.
Highest-value targets:
- Login / authentication pages — force login with attacker credentials
- Money transfer / checkout / "confirm payment" buttons
- Account settings (email change, password change, 2FA disable)
- OAuth / social-login "Authorize app" confirmation dialogs
- Admin actions (delete, promote user, change role)
Clickjacking(UI Redressing)允许攻击者在恶意站点中通过透明 iframe 加载目标页面。受害者看到的是攻击者设置的诱饵 UI,却实际点击了其下方隐藏的目标 UI。目标页面无需运行任何 JavaScript。
最高价值的目标:
- 登录 / 身份验证页面——强制受害者使用攻击者的凭据登录
- 资金转账 / 结算 / “确认支付”按钮
- 账户设置(更改邮箱、更改密码、禁用 2FA)
- OAuth / 社交登录的“授权应用”确认对话框
- 管理操作(删除用户、提升用户权限、更改角色)
Protection Headers
防护响应头
Two mechanisms prevent framing:
X-Frame-Options: DENY # strongest — blocks all framing
X-Frame-Options: SAMEORIGIN # allows same-origin frames only
Content-Security-Policy: frame-ancestors 'none' # CSP equivalent of DENY
Content-Security-Policy: frame-ancestors 'self' # CSP equivalent of SAMEORIGINIf NEITHER is present, the page is frameable from any origin.
以下两种机制可以防止页面被嵌入框架:
X-Frame-Options: DENY # strongest — blocks all framing
X-Frame-Options: SAMEORIGIN # allows same-origin frames only
Content-Security-Policy: frame-ancestors 'none' # CSP equivalent of DENY
Content-Security-Policy: frame-ancestors 'self' # CSP equivalent of SAMEORIGIN如果两者均不存在,则该页面可以被任意来源的页面嵌入框架。
How to Test
测试方法
Header-absence is the trigger for investigation, not the finding. Two steps:
Step 1 — Header check (screening). Fetch the target page and inspect the response headers:
curl -sI https://target.example/account/transfer | grep -iE 'x-frame-options|content-security-policy'If BOTH and CSP are absent, the page is a candidate. If either is present and restrictive (//), stop — it's protected.
X-Frame-Optionsframe-ancestorsDENYSAMEORIGINframe-ancestors 'none'|'self'Step 2 — Prove it actually frames and clicks (required for a real finding). Build a minimal PoC and load it in a real browser:
html
<!doctype html>
<h1>Win a prize — click below</h1>
<iframe src="https://target.example/account/transfer"
style="opacity:0.1;position:absolute;top:0;left:0;width:1000px;height:800px"></iframe>Confirm ALL of the following, or it is not exploitable:
- The page actually renders inside the iframe (no framebusting JS that blanks/redirects it — e.g. breakout, or a
if(top!==self)/JS frame check).Sec-Fetch-Dest - The sensitive action still works while framed — critically, the action must succeed cross-site. If it relies on a session cookie set or
SameSite=Lax(the modern default), the cookie is not sent on the cross-site framed request, and the clickjack fails. Verify the victim's authenticated state carries into the frame.SameSite=Strict - The target is a state-changing action (transfer, settings/email/password change, 2FA disable, OAuth authorize, admin action), not a read-only page.
Strategy: target the most sensitive action pages first — severity scales directly with what the victim is tricked into doing.
缺少响应头只是触发调查的条件,而不是漏洞发现本身。测试分为两个步骤:
第 1 步——响应头检查(初筛)。 抓取目标页面并检查响应头:
curl -sI https://target.example/account/transfer | grep -iE 'x-frame-options|content-security-policy'如果 X-Frame-Options 和 CSP frame-ancestors 两者均不存在,则该页面是一个候选项。如果其中任一响应头存在且具有严格限制(//),则可以停止——页面已受到保护。
DENYSAMEORIGINframe-ancestors 'none'|'self'第 2 步——证明页面确实可以被嵌入且点击能够生效(构成实际漏洞发现的必要条件)。 构建最小化 PoC,并在真实浏览器中加载:
html
<!doctype html>
<h1>Win a prize — click below</h1>
<iframe src="https://target.example/account/transfer"
style="opacity:0.1;position:absolute;top:0;left:0;width:1000px;height:800px"></iframe>必须确认以下所有条件,否则不构成可利用漏洞:
- 页面确实能够在 iframe 内渲染(不存在会将页面清空或重定向的 framebusting JS——例如 跳出检测,或
if(top!==self)/JS 框架检查)。Sec-Fetch-Dest - 敏感操作在被嵌入框架时仍然有效——关键是该操作必须能够跨站成功。如果操作依赖设置为 或
SameSite=Lax的会话 Cookie(现代默认值),该 Cookie 不会在跨站框架请求中发送,Clickjacking 攻击就会失败。需要验证受害者的身份验证状态能够传递到框架内。SameSite=Strict - 目标是状态变更操作(转账、设置更改、邮箱或密码更改、禁用 2FA、OAuth 授权、管理操作),而不是只读页面。
策略: 优先测试最敏感的操作页面——漏洞严重程度直接取决于受害者被诱骗执行的操作。
False Positives
误报
- Public, read-only pages (home/marketing) lacking frame protection are low/informational — no sensitive action to redress.
- APIs and non-HTML endpoints (JSON, images) are not clickjacking targets.
- Header-absence alone is NOT a finding. SameSite cookies, framebusting JS, or the lack of any sensitive framed action can each fully defeat it — which is why Step 2 is mandatory.
- 对于首页 / 营销页等缺少框架防护的公开只读页面,风险通常为低危或信息级——没有可供劫持的敏感操作。
- API 和非 HTML 端点(JSON、图片等)不是 Clickjacking 的测试目标。
- 仅凭响应头缺失不能构成漏洞发现。 SameSite Cookie、framebusting JS 或框架内不存在敏感操作,任何一项都可能完全阻止攻击——因此第 2 步是必需的。
Proof Requirements
证明要求
A valid clickjacking report shows: (1) the target page rendered inside an attacker-controlled iframe in a real browser, (2) a sensitive state-changing action reachable by a framed click while the victim is authenticated (cookies survive the cross-site context), and (3) a screenshot/recording of the overlay. Reporting missing headers with no working frame PoC is a documentation-quality issue, not a vulnerability.
一份有效的 Clickjacking 报告应证明:(1)目标页面在真实浏览器中成功渲染于攻击者控制的 iframe 内;(2)受害者在已通过身份验证时,可以通过框架内的点击触发敏感的状态变更操作(Cookie 在跨站上下文中仍会发送);(3)提供覆盖层的截图或录屏。如果仅报告响应头缺失,却没有可正常工作的框架嵌入 PoC,则这属于文档质量问题,而不是安全漏洞。