Defaults.Exposed › 修复 › 点击劫持防护(X-Frame-Options)
如何修复 点击劫持防护(X-Frame-Options)
这是一条简单的指令,告诉浏览器不允许别的网站偷偷把你的网站加载到它们自己的页面里。少了它,骗子就能把你真实的、已登录的页面藏在一个假页面后面,诱骗你的客户点击他们根本不打算点的东西——批准一笔付款、修改密码、授予权限。
对您业务的关键影响: 诈骗者可以神不知鬼不觉地把你正在运营的网站套进一个假页面里,从已登录的客户那里偷走钱或账户权限——而在客户眼里,这一切像是你的网站干的。修复是免费的,开发人员大约 15 分钟就能搞定;不加这条防护是一个已知漏洞,犯罪分子和谨慎的买家都能在几秒钟内发现。
这会让您付出什么代价
- 骗子把你真实的登录页或付款页藏在一个看似无害的页面后面,诱骗客户在毫不知情的情况下确认转账或更改设置——客户怪的是你,不是攻击者。
- 你已登录的账户区被悄悄叠加在一个写着你中奖了——点击领取的页面上方;这一点其实批准了客户账户上的一项真实变更,而你要接那通愤怒的客服电话。
- 潜在客户的 IT 团队在签约前做一次快速安全扫描,发现任何人都能把你的网站嵌入进去,于是把它标记为风险,让交易拖延甚至告吹。
- 你的品牌成了诈骗活动里的诱饵;上当的客户记住的是那家网站放任这种事发生的公司。
- 审计方或网络保险扫描把缺失的点击劫持防护列为基础安全失职——修起来很便宜,被点名却很难堪。
为什么它重要。 这是一项免费、只需一行、几分钟就能加上的设置,却能堵住一整类针对你已登录客户的伎俩。在我们的评分中,它是一项实打实得分的网页安全检查,定为高严重级,因为缺失这条响应头会留下一个已知且极易被发现的漏洞,犯罪分子会自动化利用,买家也会专门去看。
一句话说清这是什么
当有人访问你的网站时,他们的浏览器也可以被指示把你的网站加载到另一个网站里面——就像在一个大页面里嵌入一个小窗口。这听上去没什么害处,有时确实如此。但这正是一种叫点击劫持的攻击所依赖的机制。
诀窍是这样的。骗子建好自己的页面,悄悄把你真实的网站加载进去——隐形的,被设成完全透明。然后他们在上面铺上自己的内容:一个炫目的按钮、一个播放视频、一个领取你的奖品。你的客户看到的是攻击者的页面,点的是看上去无害的按钮。但因为你真实的网站正隐形地躺在他们鼠标的下方,这一点其实落在了你的页面上——确认一笔付款、修改密码、批准访问、接受一项权限。客户以为点了这个;其实点的是另一个,而且是在一个他们信任的网站上。
点击劫持防护是你网站发给每位访客浏览器的一条简短、隐形的指令,意思大致是:
不要让别的网站把我加载到它们内部。如果有谁这么干,拒绝它。
现代浏览器会自动遵从这条指令。一旦开启,这套伎俩就直接失效——被嵌入的那份你网站的副本拒绝加载。不开的话,你的网站就成了诈骗里那一层隐藏页面的现成素材,而上当的客户会把整件事联想到你的品牌,而不是攻击者的。
这会让你付出什么代价
下面是真实、日常的场景。我们从不点名任何真实企业;这些只是说明这个漏洞会被怎样利用。
-
**隐形的确认。**客户在一个标签页里登录着你的账户门户。他们落到某个页面(来自广告、邮件、搜索结果),上面承诺了某种诱人的东西,并显示一个大大的继续按钮。藏在那个按钮下方的,是从他们自己已登录的会话里加载出来的你真实的确认转账或修改邮箱控件。他们点击继续——却在不知情中授权了对他们与你之间真实账户的一项变更。在他们和你的客服团队看来,都像是他们在你的网站上自己做的。
-
**设置被劫持。**攻击者把你的账户设置页套进框架,再叠上一个无害的小游戏或问卷。在恰当位置点几下,就悄悄翻转了一项设置——把攻击者的邮箱加为找回地址、授予某个应用权限、或关闭一条安全告警。账户就此被悄无声息地攻破,而当时一切看上去都没问题。
-
**拖住的交易。**一个较大的客户在签约前发来他们标准的安全问卷。其中一行问你的网站是否设置了防嵌入保护(X-Frame-Options / CSP frame-ancestors)。你的 IT 联系人只能回答没有,于是采购流程暂停,你手忙脚乱地去补一个免费、15 分钟的设置——而它如今在买家面前看起来像个危险信号。
-
**品牌沦为诱饵的活动。**因为你真实、被信任的页面可以被嵌入,攻击者就把你的登录或结账页当作更大规模钓鱼活动里那层最有说服力的页面。上当的客户不会怪那个隐于幕后的攻击者——他们记住的是你的网站让他们被骗的那一次。
-
**审计被点名。**保险公司的扫描,或审查你安全状况的审计方,把缺失的点击劫持防护列入发现项。这是教科书级的基础安全条目;被它点名,说明那些简单、免费的防护没有到位——而这会影响外界对你其余安全状况的评判。
贯穿始终的一点:伤害落在一个真实的、已登录的客户身上,他做了自己本不打算做的事——而这件事挂的是你的名字,不是攻击者的。
它实际上是什么
当浏览器向你的网站请求一个页面时,你的服务器会连同页面一起回送一些隐形的响应头——浏览器会读取、但访客永远看不到的额外指令。点击劫持防护就是通过这些响应头来传递的。一共有两条,只要存在其中一条,我们的检查就通过:
1. 较老的响应头——X-Frame-Options:
X-Frame-Options: SAMEORIGIN
这是长期以来、被广泛支持的控制。它取两个实用取值之一:
SAMEORIGIN——你自己的网站可以嵌入自己的页面,但外部网站不能。对几乎所有人来说都是安全的默认值。DENY——任何人都不能嵌入你的页面,包括你自己。只有当你的网站从不嵌入自身内容时才用它。
2. 现代的响应头——Content-Security-Policy 的 frame-ancestors:
Content-Security-Policy: frame-ancestors 'self';
这是更新、更灵活的控制,也是现行标准所指向的那一个。它做的是同样的工作,但让你能精确指定谁可以嵌入你:
frame-ancestors 'self'——等价于SAMEORIGIN。frame-ancestors 'none'——等价于DENY。frame-ancestors 'self' https://partner.example.com——你自己的网站,外加一个指定的、受信任的合作伙伴,其他任何人都不行。
良好的状态长什么样
最稳妥的做法是两者都用:用 frame-ancestors 服务现代浏览器(并借助它精确指定允许的嵌入者),再用 X-Frame-Options: SAMEORIGIN 作为旧客户端的兜底。我们的检查只要有其一即可通过——但既然两者都免费、所花时间也一样,没理由不都设上。
有一个细节你的开发人员应该知道:Content-Security-Policy-Report-Only 响应头不会强制执行任何东西——它只上报。如果你想让点击劫持防护真正生效,它必须来自一个强制执行的响应头(一条普通的、带 frame-ancestors 的 Content-Security-Policy,或 X-Frame-Options),而不是仅上报的那个。
如何修复(免费,约 15 分钟)
**把这一节交给负责你网站的人——你的 IT 人员、网页开发人员或主机服务商。修复是免费的。**它无非是一两条响应头,或 CDN 里的一条规则。没有任何东西要花钱买。
只要存在一条 X-Frame-Options 响应头(设为 DENY 或 SAMEORIGIN)或一条 CSP 的 frame-ancestors 指令,检查就通过。推荐的双保险做法是两者都加。
第一步——决定要多严
- 大多数企业:
SAMEORIGIN/frame-ancestors 'self'。你自己的网站照常运作;外部被挡住。 - 如果你的网站从不嵌入自己的页面:
DENY/frame-ancestors 'none',达到最大程度的封锁。 - **如果某个真正的合作伙伴必须嵌入某个特定页面:**用
frame-ancestors 'self' https://partner.example.com;明确点名他们,其他任何人都进不来。
第二步——添加响应头(选你的平台)
Nginx——在你的 server 块内:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors 'self';" always;
Apache——确认已启用 mod_headers,然后在你的虚拟主机中:
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Content-Security-Policy "frame-ancestors 'self';"
Microsoft IIS——在 web.config 的 <customHeaders> 内:
<add name="X-Frame-Options" value="SAMEORIGIN" />
<add name="Content-Security-Policy" value="frame-ancestors 'self';" />
**Cloudflare(或类似的 CDN):**进入 Rules → Transform Rules → Modify Response Header,添加两条规则,对所有响应把 X-Frame-Options 设为 SAMEORIGIN、把 Content-Security-Policy 设为 frame-ancestors 'self';。如果你没有直接的服务器访问权限,这是最简单的路子。
**已经因为其他原因在发送 Content-Security-Policy?**不要再建第二条 CSP 响应头——把 frame-ancestors 加进你现有的策略里。两条 CSP 响应头可能彼此冲突。
**网站建站平台(Squarespace、Wix、Shopify 等):**这些平台往往会替你设好防嵌入保护,所以你可能已经通过、什么都不用做。如果我们的检查标记了它,相关控制通常在平台的安全设置里,或者你可以在网站前面的 CDN 上加。(注意:Google Workspace 和 Microsoft 365 驱动的是你的邮箱,不是你的网站——这条响应头要设在你公开网站实际所在的地方,而不是 Workspace/365 的管理后台。)
第三步——重载并验证
重载 Web 服务器或部署 CDN 规则,然后打开你正式运营的网站,检查响应头——浏览器开发者工具 → 网络(Network)标签页 → 点击页面请求 → 响应头(Response Headers),或用任意免费的响应头检查工具。确认这些响应头出现在真实的页面响应上,而不仅仅是首页。然后重新运行检查。
常见错误
- 用了仅上报的 CSP,却以为它在保护你。
Content-Security-Policy-Report-Only只上报违规——什么都不强制执行。要让防护生效,你需要一个强制执行的响应头。 - **设置了两条各自独立的
Content-Security-Policy响应头。**如果你已经有一条 CSP,就把frame-ancestors加进去,而不是再发一条策略;相互冲突的 CSP 响应头可能产生意外行为。 - 在自己网站会嵌入自身页面时设了
DENY。DENY会挡住所有嵌入,包括你自己的。如果你网站的任何部分使用了对自身的 iframe,改用SAMEORIGIN/frame-ancestors 'self',否则你会弄坏自己的页面。 - **只保护了首页。**对点击劫持最要紧的页面是已登录的那些——账户设置、付款确认、管理后台。务必让响应头覆盖全站,而不只是首页。
- **以为 HTTPS 或小锁已经把这事覆盖了。**加密和防嵌入毫不相干。一张完美的证书对阻止你的页面被嵌入毫无作用。
- **依赖陈旧的变通办法。**框架破除(试图跳出框架的脚本)这类 JavaScript 不可靠且可被绕过。响应头才是正确、由浏览器强制执行的修复方式。
常见问题
我不懂技术——我自己能处理吗?
技术部分不用你来做。这只是在你网站的服务器或 CDN(内容分发网络)上加一条设置,任何网页开发人员或 IT 服务商几分钟就能加好。把下面的如何修复一节交给他们——里面写清楚了要加什么。修复是免费的;只有在你希望我们持续监控它是否一直生效时,我们才收费。
这会不会让我自己的网站、或正当合作伙伴无法显示我的页面?
只有设得太严才会。常用的设置(SAMEORIGIN,或 frame-ancestors self)仍允许你自己的网站正常嵌入自己的页面——它只挡外部网站。如果某个真正的合作伙伴需要嵌入你某一个特定页面,你的开发人员可以单独放行那一个来源,同时继续挡住其他所有人。
我们是小企业——真的会有人来盯上我们吗?
这类攻击是由自动化工具批量发起的,不是逐个挑选目标。小网站常常恰恰因为缺少这类基础防护才中招。攻击者不需要知道你是谁——他们只需要你的网站可以被嵌入。堵上这个漏洞不花你一分钱。
良好的状态到底长什么样?
要么是设为 SAMEORIGIN(或 DENY)的 X-Frame-Options 响应头,要么是带有 frame-ancestors 指令的 Content-Security-Policy——理想情况下两者都有。只要有其中一个,我们的检查就通过。更现代、更灵活的控制是 frame-ancestors;X-Frame-Options 是更老的响应头,仍能覆盖一些旧浏览器,所以双保险的做法是两者都设。
这跟 SSL 小锁或 HTTPS 不是一回事吗?
不是——它们防的是完全不同的东西。HTTPS(加密传输)给连接加密,让别人无法在传输途中读取它。点击劫持防护则是从根本上阻止你的页面被加载到别人的网站里。你可以有一把完美的小锁,却仍然对点击劫持门户大开。它们是两项独立的检查,你两者都要。
如果我们不修,会拉低评分吗?
会。这是一项计分的网页安全检查,不是提示性的——缺失这条响应头会扣分,并定为高严重级,因为它直接把你已登录的客户暴露在欺诈之下。它也是最便宜能补回来的分数之一:一条免费的响应头,开发人员约 15 分钟。