2025年可靠检测第三方Cookie屏蔽的技术指南

本文详细介绍了如何在现代浏览器环境下可靠检测第三方Cookie屏蔽,涵盖主流浏览器的处理机制、多种检测技术对比,以及实用的iframe实现方案和优雅降级策略,帮助开发者提升用户体验。

可靠检测第三方Cookie屏蔽的技术指南

随着网络隐私意识的增强,第三方Cookie正逐渐被淘汰。然而,许多关键网络功能(如单点登录、欺诈预防和嵌入式服务)仍依赖Cookie正常运行。因此,检测第三方Cookie屏蔽不仅是良好的技术实践,更是用户体验的第一道防线。

为何Cookie检测仍然重要

理想的解决方案是尽快采用隐私优先的替代方案重新设计集成。但在现实中,迁移可能需要数月甚至数年,特别是对于遗留系统或第三方供应商。与此同时,用户已在禁用第三方Cookie的情况下浏览,且往往不知道功能缺失。

例如,旅行预订平台嵌入第三方合作伙伴的iframe以显示实时火车或航班时刻表。该嵌入式服务使用其自身域上的Cookie对用户进行身份验证并个性化内容(如显示保存的行程或忠诚度奖励)。但当浏览器阻止第三方Cookie时,iframe无法访问该数据。用户看到的不是无缝体验,而是错误、空白屏幕或无法正常工作的登录提示。

检测第三方Cookie屏蔽不仅是良好的技术卫生,更是用户体验的前线防御。

为何难以判断第三方Cookie是否被阻止

检测第三方Cookie是否受支持并不像调用navigator.cookieEnabled那么简单。即使是善意的检查也可能看起来安全,但无法提供实际需要的信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 无法检测第三方Cookie阻止
function areCookiesEnabled() {
  if (navigator.cookieEnabled === false) {
    return false;
  }

  try {
    document.cookie = "test_cookie=1; SameSite=None; Secure";
    const hasCookie = document.cookie.includes("test_cookie=1");
    document.cookie = "test_cookie=; Max-Age=0; SameSite=None; Secure";

    return hasCookie;
  } catch (e) {
    return false;
  }
}

此功能仅确认Cookie在当前(第一方)上下文中有效。它没有说明第三方场景(如另一个域上的iframe)的情况。更糟糕的是,它具有误导性:在某些浏览器中,即使Cookie被阻止,navigator.cookieEnabled在第三方iframe内部仍可能返回true。其他浏览器可能表现不同,导致检测不一致和不可靠。

这些跨浏览器的不一致性,加上document.cookie的限制,清楚地表明检测没有捷径。要真正检测第三方Cookie阻止,我们需要了解不同浏览器在嵌入式第三方上下文中的实际行为。

现代浏览器如何处理第三方Cookie

现代浏览器的行为直接影响哪些检测方法有效,哪些会静默失败。

Safari:完全阻止第三方Cookie

自13.1版本以来,Safari默认阻止所有第三方Cookie,无例外,即使用户之前与嵌入式域有过交互。此策略是智能跟踪预防(ITP)的一部分。

对于需要Cookie访问的嵌入式内容(如SSO iframe),Safari公开了存储访问API,需要用户手势才能授予存储权限。因此,在Safari中,第三方Cookie支持的测试几乎总是失败,除非iframe通过此API显式请求访问。

Firefox:按设计进行Cookie分区

Firefox的全面Cookie保护按站点隔离Cookie。第三方Cookie仍可设置和读取,但它们按顶级站点分区,意味着同一第三方在siteA.com和siteB.com上设置的Cookie是分开存储且无法共享。

自Firefox 102起,此行为在增强跟踪保护的标准(默认)模式下默认启用。与严格模式(完全阻止第三方Cookie,类似于Safari)不同,标准模式不会完全阻止它们。相反,它通过按站点隔离它们来中和其跟踪能力。

因此,即使测试显示第三方Cookie已成功设置,由于此分区,它可能对跨站点登录或共享会话无用。检测逻辑需要考虑这一点。

Chrome:从弃用计划到隐私沙盒(及行业反弹)

基于Chromium的浏览器仍默认允许第三方Cookie——但情况正在变化。从Chrome 80开始,第三方Cookie必须显式标记为SameSite=None; Secure,否则将被拒绝。

2020年1月,Google宣布计划在2022年逐步淘汰第三方Cookie。然而,时间表多次更新,首先在2021年6月,公司将推出时间推迟到2023年中开始,并在该年底结束。随后在2022年7月、2023年12月和2024年4月进行了额外推迟。

2024年7月,Google澄清没有计划单方面弃用第三方Cookie或在未经同意的情况下强制用户采用新模型。相反,Chrome正在转向用户选择界面,允许个人决定是否全局阻止或允许第三方Cookie。

这一变化部分受到广告行业的强烈反弹以及持续监管监督的影响,包括英国竞争与市场管理局(CMA)对Google隐私沙盒计划的审查。CMA在2025年更新中确认,无意强制弃用或触发自动Cookie阻止提示。

截至目前,第三方Cookie在Chrome中默认仍启用。新的面向用户的控件和更广泛的隐私沙盒生态系统仍处于各种实验和有限推出阶段。

Edge(基于Chromium):以跟踪器为重点的阻止与用户可配置性

Edge(基于Chromium的浏览器)共享Chrome对第三方Cookie的处理,包括SameSite=None; Secure要求。此外,Edge引入了跟踪预防模式:基本、平衡(默认)和严格。在平衡模式下,它使用Microsoft维护的列表阻止已知的第三方跟踪器,但允许许多未分类为跟踪器的第三方Cookie。严格模式比平衡模式阻止更多资源加载,可能导致某些网站行为不符合预期。

其他浏览器:它们如何?

以隐私为重点的浏览器,如Brave,默认阻止第三方Cookie作为其强反跟踪立场的一部分。

Internet Explorer(IE)11根据用户隐私设置和隐私偏好平台(P3P)标头的存在允许第三方Cookie。然而,IE使用现在可以忽略不计。值得注意的是,IE中的默认“中等”隐私设置可能阻止第三方Cookie,除非存在有效的P3P策略。

旧版本的Safari有部分第三方Cookie限制(如“允许我访问的网站”),但如前所述,这已被通过ITP的完全阻止所取代。

截至2025年,所有主要浏览器要么默认阻止或隔离第三方Cookie,除了Chrome,在其新用户选择模型推出之前,标准浏览模式下仍允许它们。

为了考虑这些变化,您的检测策略必须基于真实世界测试——特别是通过在跨源域上的iframe中加载脚本来重现真正的第三方上下文——而不是依赖浏览器名称或版本。

检测技术概述

多年来,许多技术被用于检测第三方Cookie阻止。大多数不可靠或已过时。以下是哪些方法无效(及原因)和哪些有效的快速回顾。

基本JavaScript API检查(误导性)

如前所述,主页面上的navigator.cookieEnabled或设置document.cookie不反映跨站点Cookie状态:

  • 在第三方iframe中,navigator.cookieEnabled通常返回true,即使Cookie被阻止。
  • 在父页面中设置document.cookie不测试第三方上下文。

这些检查仅适用于第一方。避免将它们用于检测。

通过localStorage的存储黑客(过时)

以前,一些开发人员通过检查window.localStorage在第三方iframe内部是否工作来推断Cookie支持——这对于阻止所有第三方存储的旧Safari版本特别有用。

现代浏览器通常允许localStorage,即使Cookie被阻止。这导致误报且不再可靠。

服务器辅助Cookie探测(重量级)

一种经典方法涉及通过HTTP从第三方域设置Cookie,然后检查是否返回:

  • 从设置Cookie的第三方服务器加载脚本/图像。
  • 立即加载另一个资源,服务器检查是否发送了Cookie。

这有效,但它:

  • 需要自定义服务器端逻辑,
  • 依赖HTTP缓存、响应标头和Cookie属性(SameSite=None; Secure),以及
  • 增加开发和基础设施复杂性。

虽然这在技术上是有效的,但不适合仅前端方法,这是我们的重点。

存储访问API(补充信号)

document.hasStorageAccess()方法允许嵌入式第三方内容检查是否有访问未分区Cookie的权限:

  • Chrome:从版本119开始支持hasStorageAccess()requestStorageAccess()。此外,从版本125开始,hasUnpartitionedCookieAccess()可用作hasStorageAccess()的别名。
  • Firefox:支持hasStorageAccess()requestStorageAccess()方法。
  • Safari:支持存储访问API。然而,访问必须始终由用户交互触发。例如,即使调用requestStorageAccess()而没有直接用户手势(如点击)也会被忽略。

Chrome和Firefox也支持API,在这些浏览器中,它可能自动工作或基于浏览器启发式或站点参与度。

此API特别适用于检测Cookie存在但分区的情况(如Firefox的全面Cookie保护),因为它有助于确定iframe是否有不受限制的Cookie访问权限。但目前,它仍最好用作补充信号,而不是独立检查。

iFrame + postMessage(最佳实践)

尽管存在存储访问API,在撰写本文时,这仍是最可靠和浏览器兼容的方法:

  • 从第三方域嵌入隐藏的iframe。
  • 在iframe内部,尝试设置测试Cookie。
  • 使用window.postMessage向父页面报告成功或失败。

这种方法在所有主要浏览器中有效(当正确配置时),不需要服务器(某种程度上,稍后详述),并模拟真实世界的第三方场景。

我们接下来将逐步实现此方法。

奖励:Sec-Fetch-Storage-Access

Chrome(从版本133开始)引入了Sec-Fetch-Storage-Access,一个HTTP请求标头,随跨站点请求发送,指示iframe是否有访问未分区Cookie的权限。此标头仅对服务器可见,无法通过JavaScript访问。它对后端分析有用,但不适用于客户端Cookie检测。

截至2025年5月,此功能仅在Chrome中实现,其他浏览器不支持。然而,知道它是不断发展的生态系统的一部分仍然很好。

逐步:通过iFrame检测第三方Cookie

那么,当我说我们看的最后一种方法“不需要服务器”时,是什么意思?虽然此方法不需要任何后端逻辑(如服务器设置的Cookie或响应检查),但它确实需要访问单独的域——或至少跨站点子域——来模拟第三方环境。这意味着:

  • 您必须从不同域或公共子域提供测试页面,例如example.com和cookietest.example.com,
  • 域需要HTTPS(以便SameSite=None; Secure Cookie工作),以及
  • 您需要托管一个简单的静态文件(测试页面),即使不涉及服务器代码。

一旦设置完成,其余逻辑完全在客户端。

步骤1:创建Cookie测试页面(在第三方域上)

最小版本(例如,https://cookietest.example.com/cookie-check.html):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
  <body>
    <script>
      document.cookie = "thirdparty_test=1; SameSite=None; Secure; Path=/;";
      const cookieFound = document.cookie.includes("thirdparty_test=1");
    
      const sendResult = (status) => window.parent?.postMessage(status, "*");
    
      if (cookieFound && document.hasStorageAccess instanceof Function) {
        document.hasStorageAccess().then((hasAccess) => {
          sendResult(hasAccess ? "TP_COOKIE_SUPPORTED" : "TP_COOKIE_BLOCKED");
        }).catch(() => sendResult("TP_COOKIE_BLOCKED"));
      } else {
        sendResult(cookieFound ? "TP_COOKIE_SUPPORTED" : "TP_COOKIE_BLOCKED");
      }
    </script>
  </body>
</html>

确保页面通过HTTPS提供,且Cookie使用SameSite=None; Secure。没有这些属性,现代浏览器将静默拒绝它。

步骤2:嵌入iFrame并监听结果

在您的主页上:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function checkThirdPartyCookies() {
  return new Promise((resolve) => {
    const iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    iframe.src = "https://cookietest.example.com/cookie-check.html"; // 您的子域
    document.body.appendChild(iframe);

    let resolved = false;
    const cleanup = (result, timedOut = false) => {
      if (resolved) return;
      resolved = true;
      window.removeEventListener('message', onMessage);
      iframe.remove();
      resolve({ thirdPartyCookiesEnabled: result, timedOut });
    };

    const onMessage = (event) => {
      if (["TP_COOKIE_SUPPORTED", "TP_COOKIE_BLOCKED"].includes(event.data)) {
        cleanup(event.data === "TP_COOKIE_SUPPORTED", false);
      }
    };

    window.addEventListener('message', onMessage);
    setTimeout(() => cleanup(false, true), 1000);
  });
}

示例用法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
checkThirdPartyCookies().then(({ thirdPartyCookiesEnabled, timedOut }) => {
  if (!thirdPartyCookiesEnabled) {
    someCookiesBlockedCallback(); // 第三方Cookie被阻止。
    if (timedOut) {
      // 未收到响应(iframe可能被阻止)。
      // 可选后备UX放在这里。
      someCookiesBlockedTimeoutCallback();
    };
  }
});

步骤3:使用存储访问API增强检测

在Safari中,即使第三方Cookie被阻止,用户也可以通过存储访问API手动授予访问权限——但仅响应于用户手势。

以下是如何在您的iframe测试页面中实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<button id="enable-cookies">此嵌入式内容需要Cookie访问。点击下方继续。</button>

<script>
  document.getElementById('enable-cookies')?.addEventListener('click', async () => {
    if (document.requestStorageAccess && typeof document.requestStorageAccess === 'function') {
      try {
        const granted = await document.requestStorageAccess();
        if (granted !== false) {
          window.parent.postMessage("TP_STORAGE_ACCESS_GRANTED", "*");
        } else {
          window.parent.postMessage("TP_STORAGE_ACCESS_DENIED", "*");
        }
      } catch (e) {
        window.parent.postMessage("TP_STORAGE_ACCESS_FAILED", "*");
      }
    }
  });
</script>

然后,在父页面上,您可以监听此消息并在需要时重试检测:

1
2
3
4
5
// 在之前的同一个`onMessage`监听器中:
if (event.data === "TP_STORAGE_ACCESS_GRANTED") {
  // 可选:重试Cookie测试,或重新加载iframe逻辑
  checkThirdPartyCookies().then(handleResultAgain);
}

(奖励)纯客户端后备(不完美,但有时必要)

在某些情况下,您可能无法访问第二个域或无法托管您控制的第三方内容。这使得iframe方法不可行。

当这种情况发生时,您的最佳选择是结合多个信号——基本Cookie检查、hasStorageAccess()localStorage后备,甚至可能被动指标如加载失败或超时——来推断第三方Cookie是否可能被阻止。

重要警告:这永远不会100%准确。但是,在受限环境中,“有总比没有好”可能仍能改善UX。

以下是一个基本示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
async function inferCookieSupportFallback() {
  let hasCookieAPI = navigator.cookieEnabled;
  let canSetCookie = false;
  let hasStorageAccess = false;

  try {
    document.cookie = "testfallback=1; SameSite=None; Secure; Path=/;";
    canSetCookie = document.cookie.includes("test_fallback=1");

    document.cookie = "test_fallback=; Max-Age=0; Path=/;";
  } catch (_) {
    canSetCookie = false;
  }

  if (typeof document.hasStorageAccess === "function") {
    try {
      hasStorageAccess = await document.hasStorageAccess();
    } catch (_) {}
  }

  return {
    inferredThirdPartyCookies: hasCookieAPI && canSetCookie && hasStorageAccess,
    raw: { hasCookieAPI, canSetCookie, hasStorageAccess }
  };
}

示例用法:

1
2
3
4
5
6
7
8
inferCookieSupportFallback().then(({ inferredThirdPartyCookies }) => {
  if (inferredThirdPartyCookies) {
    console.log("Cookie可能受支持。很可能,是的。");
  } else {
    console.warn("Cookie可能被阻止或分区。");
    // 您可以通知用户或相应调整行为
  }
});

在以下情况下使用此后备:

  • 您正在构建嵌入未知站点的仅JavaScript小部件,
  • 您不控制第二个域(或团队拒绝添加一个),或
  • 您只需要对用户端行为的一些可见性(如调试UX问题)。

不要依赖它进行安全关键逻辑(如身份验证门控)!但它可能有助于定制用户体验、显示警告或决定是否尝试后备SSO流程。再次强调,有总比没有好。

第三方Cookie被阻止时的后备策略

检测被阻止的Cookie只是战斗的一半。一旦知道它们不可用,您可以做什么?以下是一些可能对您有用的实用选项:

基于重定向的流程

对于与身份验证相关的流程,从嵌入式iframe切换到顶级重定向。让用户直接在身份提供者的站点上进行身份验证,然后重定向回来。它在所有浏览器中都有效,但UX可能不那么无缝。

请求存储访问

在清晰的UI手势后使用requestStorageAccess()提示用户(Safari需要此)。使用此功能在不离开页面的情况下重新启用Cookie。

基于令牌的通信

通过以下方式直接从父页面传递会话信息到iframe:

  • postMessage(需要指定来源);
  • 查询参数(例如,iframe URL中的签名JWT)。

这完全避免依赖Cookie,但需要双方协调:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 父页面
const iframe = document.getElementById('my-iframe');

iframe.onload = () => {
  const token = getAccessTokenSomehow(); // JWT或其他任何东西
  iframe.contentWindow.postMessage(
    { type: 'AUTH_TOKEN', token },
    'https://iframe.example.com' // 设置正确的来源!
  );
};

// iframe
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://parent.example.com') return;

  const { type, token } = event.data;

  if (type === 'AUTH_TOKEN') {
    validateAndUseToken(token); // 处理JWT,初始化会话等
  }
});

分区Cookie(CHIPS)

Chrome(自版本114起)和其他基于Chromium的浏览器现在支持具有Partitioned属性的Cookie(称为CHIPS),允许按顶级站点进行Cookie隔离。这对于不需要跨站点身份的小部件(如聊天或嵌入式表单)很有用。

注意:Firefox和Safari不支持Partitioned Cookie属性。Firefox默认使用不同机制(全面Cookie保护)强制执行Cookie分区,而Safari完全阻止第三方Cookie。

但要小心,因为它们被

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计