
本文介绍一种基于 Object.defineProperty 拦截 document.cookie 写入操作并结合调用栈分析的技术方案,可在浏览器环境中可靠识别外部脚本(如来自 domain B)在当前页面(domain A)设置 Cookie 的原始来源域。
本文介绍一种基于 `object.defineproperty` 拦截 `document.cookie` 写入操作并结合调用栈分析的技术方案,可在浏览器环境中可靠识别外部脚本(如来自 domain b)在当前页面(domain a)设置 cookie 的原始来源域。
在 Web 安全与调试实践中,常遇到跨域脚本(如嵌入的第三方 SDK、广告或分析脚本)在当前站点(domain A)静默设置 Cookie 的场景。由于同源策略限制,javaScript 无法直接读取 document.cookie 的元信息(如 Domain 属性的设置者、http 响应头中的 Set-Cookie 发起源),也无法通过标准 API 获取 Cookie 的“创建者域”。但我们可以借助 javascript 运行时的执行上下文特性,主动监控 cookie 设置行为本身,从而逆向推断其来源。
核心思路是:重定义 document.cookie 的 setter,捕获每次赋值操作,并利用错误堆栈(stack trace)定位触发该操作的脚本 URL。现代浏览器(chrome、firefox、edge)在 Error.stack 中会包含调用 document.cookie = … 的脚本路径,且堆栈底部(最后一行)通常指向实际执行写入的外部脚本(如 http://domain.b/script.js)。
以下为可直接部署的调试脚本:
<script type="text/javascript"> function monitorCookieOrigin() { // 仅在开发/调试阶段启用,避免生产环境性能损耗 if (typeof document === 'undefined') return; const originalDescriptor = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie'); if (!originalDescriptor || !originalDescriptor.set) return; Object.defineProperty(document, 'cookie', { set: function(value) { // 构造错误对象以捕获当前调用栈 const err = new Error(); const stackLines = (err.stack || '').split('n'); // 提取最可能的发起脚本行(通常为倒数第二或第三行,跳过 native / set 行) let initiator = 'unknown'; for (let i = Math.min(3, stackLines.length - 1); i >= 0; i--) { const line = stackLines[i].trim(); if (line.includes('@') && (line.includes('http://') || line.includes('https://'))) { initiator = line.split('@')[1].split(':')[0]; break; } } console.info('[Cookie Origin Monitor]', { value: value.substring(0, 100), // 防止过长日志 initiator: initiator, timestamp: new Date().toISOString() }); }, get: originalDescriptor.get }); } // 立即启用监控 monitorCookieOrigin(); </script>
✅ 使用效果示例:
当 domain.b/script.js 执行 document.cookie = “session=abc123; domain=domain.a” 时,控制台将输出类似:
[Cookie Origin Monitor] { "value": "session=abc123; domain=domain.a", "initiator": "http://domain.b/script.js", "timestamp": "2024-06-15T08:22:34.123Z" }
⚠️ 重要注意事项:
立即学习“Java免费学习笔记(深入)”;
- 仅适用于 JavaScript 设置的 Cookie:该方法无法捕获通过 HTTP 响应头 Set-Cookie 直接下发的 Cookie(例如后端重定向或 iframe 加载触发的 Set-Cookie),因其不经过 document.cookie setter。
- 浏览器兼容性:Chrome、Firefox、Edge 支持良好;safari 对 Error.stack 格式支持较弱,建议配合 console.trace() 做降级处理。
- 性能与安全:重定义原生属性存在微小性能开销,且不应在生产环境长期启用;若需自动化审计,建议封装为 DevTools 扩展或 Puppeteer 脚本。
- Domain 属性误导风险:注意 document.cookie setter 中显式指定的 Domain= 参数(如 Domain=domain.a)仅代表 Cookie 作用域,并非创建者域——真正的创建者始终是执行该 JS 的脚本所在源(origin)。
总结而言,虽然浏览器未提供“Cookie 来源域”的标准 API,但通过运行时拦截 + 堆栈分析这一轻量级调试技术,开发者可精准、实时地识别跨域脚本对 Cookie 的写入行为,为安全审计、合规检查及第三方依赖治理提供关键依据。