下面我将从简单到复杂,为你介绍几种主流且有效的防护方法。

网页如何防止iframe 外连
(图片来源网络,侵删)

核心思想

所有方法的最终目的都是:控制你的网页(A.com)能否被其他网站(B.com)通过 <iframe> 嵌入。


使用 X-Frame-Options HTTP 响应头 (最推荐、最有效)

这是最直接、最强大的防护手段,通过服务器在 HTTP 响应头中发送一个指令,告诉浏览器是否允许你的页面被嵌入到 <iframe> 中。

DENY

含义:完全禁止任何网站将你的页面嵌入到 <iframe> 中,无论同源还是跨域。

配置示例 (Nginx):

网页如何防止iframe 外连
(图片来源网络,侵删)
add_header X-Frame-Options "DENY" always;

配置示例 (Apache):

<IfModule mod_headers.c>
    Header always set X-Frame-Options "DENY"
</IfModule>

适用场景: 适用于不希望被任何第三方嵌入的页面,例如用户登录页、支付确认页、后台管理界面等核心操作页面。

SAMEORIGIN

含义:只允许与你同源的网站(即协议、域名、端口都相同)将你的页面嵌入到 <iframe> 中。

配置示例 (Nginx):

网页如何防止iframe 外连
(图片来源网络,侵删)
add_header X-Frame-Options "SAMEORIGIN" always;

配置示例 (Apache):

<IfModule mod_headers.c>
    Header always set X-Frame-Options "SAMEORIGIN"
</IfModule>

适用场景: 这是最常用的配置,它允许你自己的网站(A.com 的不同页面之间)使用 <iframe> 互相嵌入,同时防止了恶意网站的跨域嵌入。

ALLOW-FROM uri

含义:只允许指定的 uri 将你的页面嵌入到 <iframe> 中。注意: 此方法在现代浏览器中已不被广泛支持,被视为遗留特性,Chrome 和 Firefox 等主流浏览器已不再支持此指令。

不推荐使用,除非你明确知道你的用户都在使用非常老旧的浏览器且没有升级计划。


使用 frame-ancestors CSP (Content Security Policy) (更现代、更灵活)

Content Security Policy (CSP) 是一个更全面的安全策略,而 frame-ancestors 是其中的一个指令,专门用于控制 <iframe> 的嵌入者,它被认为是 X-Frame-Options 的现代替代方案,功能更强大。

none

含义:与 X-Frame-Options: DENY 效果相同,完全禁止任何嵌入。

配置示例 (Nginx):

add_header Content-Security-Policy "frame-ancestors 'none';" always;

self

含义:与 X-Frame-Options: SAMEORIGIN 效果相同,只允许同源嵌入。

配置示例 (Nginx):

add_header Content-Security-Policy "frame-ancestors 'self';" always;

允许特定域名

含义:这是 X-Frame-Options 无法做到的,可以指定允许嵌入你的页面的特定域名列表。

配置示例 (Nginx):

add_header Content-Security-Policy "frame-ancestors https://trusted-site.com https://another-trusted-site.com;" always;

优点

  • 灵活性高:可以精确控制允许的域名。
  • 功能更强大:CSP 还能提供其他安全防护,如防止 XSS 攻击、数据泄露等。
  • 未来趋势:是 W3C 推荐的标准。

前端 JavaScript 检测 (防御性措施,非绝对)

这种方法在前端代码中运行,用于检测当前页面是否被嵌套在 <iframe> 中,并采取相应措施,它不能阻止 <iframe> 的加载,但可以减轻点击劫持带来的危害。

实现原理

通过 window.selfwindow.top 的比较来判断。

  • window.self:始终指向当前窗口(iframe 内部)。
  • window.top:始终指向最顶层的窗口。

如果页面被嵌入在 <iframe> 中,window.self 不等于 window.top

示例代码

你可以将这段代码放在你页面的 <head> 标签内,或者作为一个独立的脚本引入。

<script>
  if (window.self !== window.top) {
    // 如果被嵌入在 iframe 中,可以采取以下措施:
    // 方案1:在控制台输出警告
    console.warn("This page is being framed! Possible clickjacking attack.");
    // 方案2:在页面上显示一个警告横幅
    const warningBanner = document.createElement('div');
    warningBanner.style.position = 'fixed';
    warningBanner.style.top = '0';
    warningBanner.style.left = '0';
    warningBanner.style.width = '100%';
    warningBanner.style.background = 'red';
    warningBanner.style.color = 'white';
    warningBanner.style.padding = '10px';
    warningBanner.style.zIndex = '9999';
    warningBanner.textContent = '警告:此页面正被嵌入在框架中,请注意安全!';
    document.body.insertBefore(warningBanner, document.body.firstChild);
    // 方案3:强制将顶层页面跳转到当前页面,打破嵌套
    // top.location.href = window.location.href;
    // 方案4:让页面内容“消失”,只显示警告
    // document.body.style.display = 'none';
    // 然后插入 warningBanner...
  }
</script>

局限性

  • 无法完全阻止:攻击者可以在 <iframe> 中使用 sandbox 属性来禁用 JavaScript,从而绕过这种检测。
  • 用户体验差:显示警告或强制跳转可能会影响正常用户(你自己的产品线内合法的 <iframe> 嵌入)。

方法 优点 缺点 推荐度
X-Frame-Options 简单、高效、兼容性好 功能相对单一,不如 CSP 灵活 ⭐⭐⭐⭐⭐ (首选)
frame-ancestors CSP 功能强大、灵活、可扩展性好 配置稍复杂,是较新的标准 ⭐⭐⭐⭐⭐ (现代首选)
JavaScript 检测 可作为补充,提供防御性警告 无法完全阻止,易被绕过,影响体验 ⭐⭐ (作为辅助手段)

推荐的部署策略

  1. 为所有页面设置默认策略: 在你的服务器上,为所有页面(或全局)设置一个默认的 X-Frame-Options: SAMEORIGINContent-Security-Policy: frame-ancestors 'self';,这能提供基础且有效的防护。

    # Nginx 全局配置示例
    server {
        # ... 其他配置 ...
        add_header X-Frame-Options "SAMEORIGIN" always;
        # 或者
        # add_header Content-Security-Policy "frame-ancestors 'self';" always;
    }
  2. 为特定页面设置更严格的策略: 对于极其敏感的页面,如登录页、支付页、密码修改页等,覆盖默认策略,使用 DENY'none'

    # Nginx 特定路径配置示例
    location /login {
        add_header X-Frame-Options "DENY" always;
    }
  3. (可选)为特定合作伙伴页面设置例外: 如果你的业务需要与第三方网站合作,并允许他们嵌入你的某个特定页面(一个嵌入式的活动页面),那么使用 frame-ancestors CSP 并明确指定允许的域名。

    # Nginx 合作伙伴页面配置示例
    location /embedded-widget {
        add_header Content-Security-Policy "frame-ancestors https://partner-a.com https://partner-b.com;" always;
    }

请优先配置 X-Frame-Optionsframe-ancestors CSP 在你的服务器上,这是防止 <iframe> 外连最根本、最有效的办法,JavaScript 检测可以作为锦上添花的补充,但不能替代服务器端的响应头设置。