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

本文深入探讨了2025年主流浏览器对第三方Cookie的屏蔽机制,提供了基于iframe和postMessage的可靠检测方案,并详细分析了Safari、Firefox、Chrome等浏览器的不同处理策略及应对措施。

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

背景与挑战

随着隐私保护意识的增强,网络正在逐步淘汰第三方Cookie技术。万维网联盟技术架构组(W3C TAG)正积极推动从网络平台中完全移除第三方Cookie。主流浏览器(Chrome、Safari、Firefox和Edge)正在逐步淘汰第三方Cookie,但这一过渡是渐进的。

虽然这种转变增强了用户隐私,但也破坏了依赖第三方Cookie的合法功能,如单点登录(SSO)、欺诈预防和嵌入式服务。由于目前还没有普遍禁令,许多基本的网络功能仍然依赖这些Cookie,开发人员必须检测第三方Cookie何时被阻止,以便应用程序能够优雅地响应。

为什么Cookie检测仍然重要

理想的解决方案是尽快完全放弃第三方Cookie,并使用隐私优先、专门构建的替代方案重新设计我们的集成。但在现实中,这种迁移可能需要数月甚至数年时间,特别是对于遗留系统或第三方供应商。与此同时,用户已经在禁用第三方Cookie的情况下浏览,并且通常不知道缺少任何功能。

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

检测第三方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。其他浏览器可能表现不同,导致检测不一致和不可靠。

现代浏览器处理机制

Safari:完全阻止第三方Cookie

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

对于需要Cookie访问的嵌入内容(例如SSO iframe),Safari公开了存储访问API,该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。严格模式比平衡模式阻止更多资源加载,这可能导致某些网站行为不如预期。

检测技术概述

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

基本JavaScript API检查(误导性)

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

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

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

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

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

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

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

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

  1. 从设置Cookie的第三方服务器加载脚本/图像
  2. 立即加载另一个资源,服务器检查是否发送了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,但在撰写本文时,这仍然是最可靠和浏览器兼容的方法:

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

这种方法适用于所有主流浏览器(当正确配置时),不需要服务器(某种程度上,接下来会详细说明),并模拟真实的第三方场景。

逐步指南:通过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。

但要小心,因为它们被基本检测视为"被阻止”。如果需要,请优化你的逻辑。

结论与展望

第三方Cookie正在消失,尽管是逐渐且不均匀的。在过渡完成之前,作为开发人员,你的工作是弥合技术限制和现实世界用户体验之间的差距。这意味着:

关注标准 像FedCM和隐私沙盒功能(Topics、归因报告、围栏框架)这样的API正在重塑我们如何处理身份和分析,而不依赖跨站点Cookie。

将检测与优雅的备用方案相结合 无论是提供重定向流程、使用requestStorageAccess(),还是回退到基于令牌的消息传递——每一个小的UX改进都会累积。

通知你的用户 用户不应该想知道为什么某些东西在一个浏览器中有效但在另一个浏览器中静默中断。不要让他们觉得他们做错了什么——只是帮助他们继续前进。清晰、友好的消息可以防止这种混淆。

好消息?你今天不需要完美的解决方案,只需要一个有弹性的解决方案。通过早期检测问题并深思熟虑地处理它们,你可以保护你的用户和未来的架构,一次一个无Cookie浏览器。

正如Chrome从自动弃用转向所看到的那样,过渡并不总是线性的。行业反馈、监管监督和不断发展的技术现实继续塑造时间和解决方案。

不要忘记:有总比没有好。

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