当网站数量从几个增长到几百个,人工检查 Cookie 很快就会失去可操作性。更可靠的做法是构建一个 Cookie 审计器:带着明确的隐私偏好访问页面,记录页面实际加载的 Cookie,再判断这些 Cookie 是否符合用户选择。
这个思路的重点不只是“页面有没有 Cookie”,而是验证一个完整的行为契约:用户拒绝某类 Cookie 后,页面是否仍然设置了对应 Cookie;用户允许必要 Cookie 时,关键页面是否还能正常工作;不同页面、子域名和加载路径是否保持一致。
审计器应该模拟什么
一个隐私意识明确的用户通常会有一组可执行的偏好,例如:
{
"necessary": true,
"analytics": false,
"advertising": false,
"functional": true
}
审计器可以为每组偏好启动独立的浏览器上下文,然后依次访问待测页面。每次访问至少记录以下信息:
- 页面 URL 和最终跳转地址
- 响应头中的
Set-Cookie - 浏览器上下文中最终存在的 Cookie
- Cookie 的域名、路径、名称和属性
- 页面加载期间发起的第三方请求
- Cookie 与偏好类别之间的匹配结果
这样可以覆盖两类常见问题。第一类是服务端在响应头中提前写入 Cookie;第二类是 JavaScript、标签管理器或第三方脚本在页面加载后动态写入 Cookie。只检查浏览器开发者工具中的某一个时间点,很容易漏掉其中一类。
从单页检查扩展到数百个页面
规模化审计的关键是把“访问页面”和“判断结果”分开。访问器负责收集事实,规则引擎负责解释事实。这样,当 Cookie 分类规则变化时,不需要重新设计浏览器自动化流程。
可以为每个站点维护一份期望策略:
site: shop.example.test
pages:
- /
- /checkout
- /account
allowed_cookies:
necessary:
- session_id
- csrf_token
functional:
- locale
analytics: []
advertising: []
实际项目中,Cookie 名称通常需要支持正则表达式或前缀匹配,例如 ^session_。还应把第三方 Cookie 单独标记出来,因为同一个 Cookie 名称在不同域名下可能代表完全不同的行为。
批量运行时,可以把页面清单放在 CSV、数据库或配置仓库中,并为每次审计生成一个版本化报告。报告不应只给出“通过”或“失败”,还要保留发现证据,方便网站负责人定位是哪个页面、哪个域名、哪个脚本设置了不合规 Cookie。
一个可以改造的 Playwright 示例
下面是一个最小的 Python 示例。它假设页面已经提供了一个可自动化操作的同意管理界面,例如按钮带有 data-consent-analytics="reject" 属性。实际项目需要根据站点的 CMP 实现修改选择器。
安装依赖:
python -m pip install playwright
python -m playwright install chromium
保存为 cookie_audit.py 后运行:
import asyncio
import json
import sys
from pathlib import Path
from urllib.parse import urlparse
from playwright.async_api import async_playwright
async def audit_page(browser, url: str, preferences: dict) -> dict:
context = await browser.new_context()
page = await context.new_page()
response_cookies = []
async def capture_response(response):
if "set-cookie" in response.headers:
response_cookies.append({
"url": response.url,
"set_cookie": response.headers["set-cookie"],
})
page.on("response", capture_response)
try:
await page.goto(url, wait_until="networkidle", timeout=45_000)
# 选择器是示例假设,请替换为目标站点 CMP 的实际控件。
for category, allowed in preferences.items():
action = "accept" if allowed else "reject"
selector = f'[data-consent-{category}="{action}"]'
button = page.locator(selector).first
if await button.count():
await button.click()
await page.wait_for_timeout(2_000)
cookies = await context.cookies()
return {
"url": url,
"final_url": page.url,
"preferences": preferences,
"cookies": [
{
"name": cookie["name"],
"domain": cookie["domain"],
"path": cookie["path"],
"secure": cookie["secure"],
"same_site": cookie["sameSite"],
}
for cookie in cookies
],
"set_cookie_headers": response_cookies,
}
except Exception as exc:
return {"url": url, "preferences": preferences, "error": str(exc)}
finally:
await context.close()
async def main():
if len(sys.argv) != 2:
raise SystemExit("usage: python cookie_audit.py pages.txt")
urls = [line.strip() for line in Path(sys.argv[1]).read_text().splitlines() if line.strip()]
preferences = {
"analytics": False,
"advertising": False,
"functional": True,
}
async with async_playwright() as playwright:
browser = await playwright.chromium.launch(headless=True)
results = []
for url in urls:
results.append(await audit_page(browser, url, preferences))
await browser.close()
print(json.dumps(results, indent=2, ensure_ascii=False))
if __name__ == "__main__":
asyncio.run(main())
创建页面清单并执行:
printf '%s\n' \
'https://shop.example.test/' \
'https://shop.example.test/checkout' \
'https://shop.example.test/account' > pages.txt
python cookie_audit.py pages.txt > cookie-report.json
这段代码负责采集结果,并没有硬编码“哪些 Cookie 一定合规”。生产环境中可以在报告生成阶段加入规则判断:如果 analytics 为 false,则拒绝名称匹配分析类规则的 Cookie;如果 Cookie 来自不在允许列表中的第三方域名,则标记为待审查,而不是直接假设它违规。
让结果足够可信
自动化浏览器只能告诉我们页面做了什么,不能自动解决 Cookie 分类本身的业务定义。审计系统需要明确几个边界:
- “必要”应由隐私和产品负责人共同维护,不能因为某个脚本历史上一直存在就自动归入必要类别。
- Cookie 名称会因版本、地区和登录状态变化,规则应支持域名、前缀和正则表达式。
- 需要分别测试首次访问、已有同意记录、登录用户和清除站点数据后的场景。
- 同一页面可能根据地理位置或实验分组加载不同脚本,测试结果应保存运行环境和时间。
- Cookie 之外还应观察本地存储、像素请求和第三方网络请求,否则审计范围可能过窄。
- 页面加载失败、CMP 没有出现、超时等情况应单独记录,不能简单算作“没有发现 Cookie”。
在数百个页面上运行时,可以按站点和偏好组合并发,但要限制并发度,避免对生产站点造成不必要压力。对不可重复的流程,例如下单或发送表单,应使用测试环境、只读路径或专门的测试账号。
落地时的检查清单
- 定义可机器判断的 Cookie 类别和允许列表。
- 为每组用户偏好创建隔离的浏览器上下文。
- 同时采集响应头 Cookie、浏览器 Cookie 和网络请求。
- 覆盖首页、登录后页面、关键转化页面以及随机抽样页面。
- 保存 URL、最终跳转、运行环境、时间和原始证据。
- 将“违规”“待人工确认”和“技术失败”分开统计。
- 在每次部署或 CMP 配置变更后自动运行审计。
这种审计器的价值不在于生成一份漂亮的 Cookie 清单,而在于验证用户偏好是否真正影响了页面行为。把它接入发布流程后,隐私控制就从一次性的人工检查变成了可以持续回归的工程测试。