全部指南
严重程度:中危安全响应头

点击劫持与 X-Frame-Options:让你的页面远离 iframe

点击劫持通过把你的页面加载进一个透明 iframe(放在攻击者控制的网站上),诱骗用户点击页面上隐藏的控件。X-Frame-Options——或现代的 CSP frame-ancestors 指令——会告诉浏览器拒绝嵌入你的页面,从而彻底封堵这种攻击。

这是什么

点击劫持攻击的原理,是把你的网站框入攻击者页面上一个不可见或半透明的 iframe。受害者看到的是诱饵内容,点击的是看似无害的按钮;而隐藏的 iframe 捕获的是对你页面的真实点击——一笔银行转账、一次设置更改、一次 OAuth 授权——用户却毫无察觉。

X-Frame-Options 是一个 HTTP 响应头,有两个有用的取值:DENY(拒绝任何人嵌入)和 SAMEORIGIN(只允许你自己源的页面嵌入)。CSP 的 frame-ancestors 指令做的是同样的事,但控制更精细——你可以列出特定的受信任源——而且在支持 CSP 的浏览器中,它会取代 X-Frame-Options。

为什么重要

任何带有实质性操作的页面——账户设置、支付确认、邮件偏好或管理开关——都是点击劫持的目标。一个已登录的用户一旦落到恶意页面上,就可能被诱骗执行他们从未打算做的真实操作,而且没有任何明显迹象表明出了问题。

添加 X-Frame-Options 毫无成本,并且所有现代浏览器都支持。在会执行敏感操作的页面上省略它,是一个实实在在的配置错误,而不是缺失某个功能。

如何修复

在 Next.js 中全局添加 X-Frame-Options

在 next.config.js 中设置该响应头,使其应用于每个路由。对不需要被嵌入的应用来说,DENY 是安全的默认值。

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: "/:path*",
        headers: [{ key: "X-Frame-Options", value: "DENY" }],
      },
    ];
  },
};
在你的 CSP 中添加 frame-ancestors

在现代浏览器中,CSP 的 frame-ancestors 指令会取代 X-Frame-Options,并让你可以按源进行控制。把它加到你已经设置的 Content-Security-Policy 响应头中。

// extend your existing CSP string
const csp = [
  // ... your other directives ...
  "frame-ancestors 'none'",
].join("; ");
如果你在自己的域名内嵌入页面,请使用 SAMEORIGIN

如果你确实需要在自己的网站中嵌入页面——一个预览 iframe 或一个同源的嵌入式小组件——请使用 X-Frame-Options: SAMEORIGIN 而不是 DENY,或使用 frame-ancestors 'self'。

配置 nginx

如果你通过 nginx 代理提供响应,请在你的 server 块中添加该响应头。

add_header X-Frame-Options "DENY" always;

常见问题

X-Frame-Options 还是 CSP frame-ancestors——我该用哪个?

两个都用。X-Frame-Options 覆盖较旧的浏览器;CSP 中的 frame-ancestors 给你按源的精细控制,也是现代标准。它们可以安全共存:支持 CSP 的浏览器使用 frame-ancestors 并忽略 X-Frame-Options。

DENY 会破坏我自己小组件或分析仪表盘的嵌入吗?

会——DENY 会阻止一切嵌入,包括你自己的源。如果你需要在自己域名的 iframe 中嵌入某个特定页面,请使用 SAMEORIGIN 或 frame-ancestors 'self'。只对确实需要嵌入的页面放宽限制。

对于缺失的 X-Frame-Options,AppSafe 会报告什么严重程度?

中危。它本身不是一个能立刻窃取数据的途径,但任何会执行账户操作的页面,没有它都存在真实的点击劫持风险。

你的应用受影响了吗?

AppSafe 一次免费扫描,就能检查这个问题以及数十项其他问题。

免费扫描我的应用