如何通过堆栈追踪识别外部域名设置 Cookie 的来源

1次阅读

如何通过堆栈追踪识别外部域名设置 Cookie 的来源

本文介绍一种利用 javascript 属性拦截与错误分析的技术,实现在主站(domain a)中精准识别由第三方脚本(domain b)写入 cookie 的源头域名,适用于安全审计、合规监控与跨域行为分析场景。

本文介绍一种利用 javascript 属性拦截与错误堆分析的技术,实现在主站(domain a)中精准识别由第三方脚本(domain b)写入 cookie 的源头域名,适用于安全审计、合规监控与跨域行为分析场景。

在现代 Web 应用中,第三方脚本(如广告、分析、SDK 等)常通过 <script src="https://domain.b/sdk.js"> 方式嵌入主站(domain A),并在运行时调用 document.cookie = … 设置 Cookie。由于同源策略限制,<strong>JavaScript 无法直接读取或枚举已存在的 Cookie 的创建来源——document.cookie 仅返回当前域下可访问的键值对,且不附带元信息(如设置者域名、时间戳、HTTP-only 标志等)。</script>

但关键突破口在于:Cookie 的写入操作必然发生在某段 JavaScript 执行上下文中,而该上下文可通过调用堆栈(call stack)追溯到原始脚本 URL。虽然浏览器不提供原生 API 获取“谁设置了这个 Cookie”,但我们可以通过重定义 document.cookie 的 setter,捕获每次赋值动作,并借助 Error.stack 提取执行栈中最末尾(即最靠近调用点)的脚本位置。

以下为经过优化、兼容主流浏览器(chromefirefoxedge)的实用方案:

<script type="text/javascript">   function monitorCookieOrigin() {     // 仅在支持 Object.defineProperty 的环境中启用     if (!Object.defineProperty || !document) return;      Object.defineProperty(document, 'cookie', {       set: function (value) {         // 创建错误对象以捕获当前执行栈         const err = new Error();         let stack = err.stack || '';          // 清洗堆栈:移除 Error 构造提示,提取有效行         const lines = stack           .split('n')           .map(line => line.trim())           .filter(line => line && !line.startsWith('Error'));          // 取最后一行(最接近 cookie 写入点的调用者)         const lastCall = lines.length > 0 ? lines[lines.length - 1] : 'unknown';          // 输出结构化日志:Cookie 值 + 源脚本地址(含域名)         console.info('[Cookie Origin Monitor]', {           value: String(value).substring(0, 100), // 防止过长截断           originScript: lastCall.match(/@([^:s]+:[d]+)/)?.[1] || lastCall,           timestamp: new Date().toISOString()         });       },       configurable: true,       enumerable: true     });   }    // 页面加载后立即启用监控   if (document.readyState === 'loading') {     document.addEventListener('DOMContentLoaded', monitorCookieOrigin);   } else {     monitorCookieOrigin();   } </script>

示例输出(Chrome):

[Cookie Origin Monitor] {   value: "tracking_id=abc123; domain=.domain.a; path=/",   originScript: "https://domain.b/script.js:42:15",   timestamp: "2024-06-15T08:22:34.123Z" }

示例输出(Firefox):
Firefox 堆栈格式略有差异,但同样包含脚本 URL:

[Cookie Origin Monitor] {   value: "session=xyz; expires=...",   originScript: "https://domain.b/analytics.min.js line 1 > script:127",   timestamp: "2024-06-15T08:22:34.456Z" }

⚠️ 重要注意事项:

  • 仅监控写入行为,不追溯历史 Cookie:该方法仅捕获 document.cookie = … 执行瞬间的调用栈,无法回溯页面加载前已存在的 Cookie 来源。
  • 无法识别服务端 Set-Cookie 头:若 Cookie 由 domain B 的后端响应头 Set-Cookie 直接下发(如 iframe 加载、fetch 请求),此方案无效——因无 JS 执行上下文。
  • 受 CSP 与严格模式影响:若站点启用了 Content-Security-Policy: script-src ‘self’ 且未允许 ‘unsafe-eval’,部分堆栈解析可能受限;建议配合 report-uri 收集异常。
  • 生产环境慎用:重定义原生属性存在极小兼容风险,建议仅用于开发/审计阶段,或封装为条件加载模块(如 ?debug=cookies)。
  • 隐私与合规提示:监控第三方 Cookie 行为需符合 GDPR、CCPA 等法规要求,建议在用户授权前提下启用,并避免记录敏感值(如完整 Cookie 字符串可做脱敏处理)。

? 进阶建议:

  • 结合 PerformanceObserver 监听 Resource 类型,关联 domain.b/script.js 的加载时机;
  • 使用 MutationObserver 配合 <script> 节点插入事件,预埋监控逻辑,提升覆盖率; </script>
  • 将日志上报至内部审计平台,构建第三方行为图谱,辅助识别隐蔽跟踪行为。

综上,尽管浏览器未暴露 Cookie 元数据,但通过拦截 setter + 解析执行栈,我们能以高置信度定位绝大多数 JS 驱动的 Cookie 设置源头——这是前端安全可观测性中一项轻量、有效且无需后端协作的关键技术实践。

text=ZqhQzanResources