返回博客
身份

使用 BotBrowser 管理 Cookie

了解如何使用 --bot-cookies 标志在 BotBrowser 中管理 Cookie,实现会话持久性、跨会话连续性和按身份隔离。

介绍

Cookie 是浏览器维护状态的基石。它们携带会话令牌、用户偏好、同意记录和认证凭证,在页面加载和访问之间保持状态。在为隐私研究、测试或多账户管理构建浏览器配置文件时,控制 Cookie 层与控制指纹层同等重要。一个指纹一致但没有 Cookie 的浏览器每次看起来像是全新安装,这并非真实浏览器的行为。

BotBrowser 提供了 --bot-cookies 标志(PRO 级)用于在启动时预加载 Cookie。这样 Cookie 会在首次导航、任何 JavaScript 执行之前就已存在,防止跟踪脚本在检查浏览器状态时发现缺失的 Cookie。配合指纹配置文件和其他身份信号,Cookie 管理可以完善真实且持久的浏览器身份。

网站和跟踪系统不会单独评估 Cookie,而是查看 Cookie 与其他浏览器信号之间的关联。一个呈现为返回用户的指纹但没有 Cookie 的浏览器会引发怀疑;同样,一个只有会话 Cookie 但完全新的指纹也显得不一致。

对于研究跟踪系统如何关联浏览器信号的隐私研究人员而言,控制 Cookie 至关重要。它允许你测试跟踪系统是仅基于 Cookie、仅基于指纹,还是基于两者的组合来链接会话。对于多账户管理,每个身份需要自己的 Cookie 存储并在会话间持久保存,以维持登录状态并避免重复认证流程。

Cookie 管理对于测试 Cookie 同意横幅、GDPR 合规流程以及网站对返回用户与新访客的不同表现也很重要。没有预加载 Cookie,每次会话都从“新访客”开始,无法测试返回用户体验。

技术背景

Chromium 将 Cookie 存储在用户数据目录内的 SQLite 数据库中。浏览器启动时会将这些 Cookie 从数据库加载到内存。每个 Cookie 具有若干属性:

  • Domain 和 Path:决定哪些请求会携带该 Cookie
  • Name 和 Value:Cookie 的实际数据
  • Expiration:何时删除该 Cookie(会话 Cookie 没有到期时间)
  • Secure 标志:Cookie 是否仅通过 HTTPS 发送
  • HttpOnly 标志:JavaScript 是否可访问该 Cookie
  • SameSite:控制跨站 Cookie 行为(Strict、Lax 或 None)

页面加载时,浏览器会将匹配的 Cookie 附加到每个 HTTP 请求。页面上的 JavaScript 也可以通过 document.cookie 读取和写入 Cookie(受 HttpOnly 限制)。

时序问题

大多数自动化框架通过其 API 提供 Cookie 管理。例如 Playwright 有 context.addCookies(),Puppeteer 有 page.setCookie()。但是这些方法是在浏览器上下文或页面创建之后添加 Cookie,因此首次网络请求(包括初始页面加载和任何预检请求)会在没有这些 Cookie 的情况下发出。

这个时序差距很重要,因为一些跟踪系统会检查初始请求中的 Cookie 存在与否。如果首次请求没有 Cookie,但后续请求带有 Cookie,服务器端日志中会暴露出不一致。

BotBrowser 的 --bot-cookies 标志通过在引擎初始化期间注入 Cookie 来解决此问题,使 Cookie 在首次请求时就可用。

常见方法及其局限

Playwright 和 Puppeteer 都通过其 API 提供 Cookie 管理。它们在许多用例下有效,但存在局限:

  1. 时序晚:通过框架 API 添加的 Cookie 只有在上下文或页面创建后才可用,错过初始页面加载
  2. 与上下文绑定:Cookie 与浏览器上下文相关联,不是浏览器实例级别。除非显式配置,否则同一浏览器实例内的多个上下文不会共享 Cookie
  3. 格式差异:每个框架使用略有不同的 Cookie 对象格式,难以在工具间共享 Cookie 数据

手动持久化用户数据目录

另一种方法是跨会话重用相同的 --user-data-dir。这会自动保留 Cookie,因为它们保存在 SQLite 数据库中,但同时也会保留缓存、本地存储、IndexedDB、service worker 和浏览历史。该方法无法对保留哪些状态进行细粒度控制。

浏览器扩展可以管理 Cookie,但它们运行在错误的权限级别,无法在首次页面加载前注入 Cookie,并且扩展本身也是可检测的信号。

BotBrowser 的方法

BotBrowser 通过 --bot-cookies 标志在引擎层实现 Cookie 预加载。该设计相对于上述替代方案有若干优势。

引擎级注入

通过 --bot-cookies 加载的 Cookie 在初始化期间插入到浏览器的 Cookie 存储中,早于任何浏览器上下文或页面加载。这意味着:

  • 首次对任何域的 HTTP 请求已携带相应的 Cookie
  • 页面加载时运行的 JavaScript 可通过 document.cookie 读取 Cookie
  • 服务器端的跟踪系统从首次请求开始就看到一致的 Cookie 存在

灵活的输入格式

--bot-cookies 标志接受两种方式的 Cookie 数据:

内联 JSON 适用于小规模 Cookie 集或动态生成:

--bot-cookies='[{"name":"session","value":"abc123","domain":".example.com"}]'

文件引用 使用 @ 前缀适用于较大的 Cookie 集:

--bot-cookies="@/path/to/cookies.json"

文件应包含一个 Cookie 对象数组:

[
  {
    "name": "session_id",
    "value": "abc123def456",
    "domain": ".example.com",
    "path": "/",
    "expirationDate": 1774000000,
    "secure": true,
    "httpOnly": true,
    "sameSite": "Lax"
  },
  {
    "name": "user_pref",
    "value": "dark_mode=1&lang=en",
    "domain": ".example.com",
    "path": "/",
    "expirationDate": 1774000000,
    "secure": false,
    "httpOnly": false,
    "sameSite": "None"
  }
]

每个身份的隔离

每个 BotBrowser 实例都有自己的 Cookie 存储。通过将不同的 Cookie 文件与不同的指纹配置文件配对,可以创建完全隔离的浏览器身份,其中指纹层和 Cookie 层均独立。

配置与使用

基本 CLI 用法

# 从文件加载 Cookie
./chrome \
  --bot-profile=/path/to/profile.enc \
  --bot-cookies="@/path/to/cookies.json"

# 内联 Cookies
./chrome \
  --bot-profile=/path/to/profile.enc \
  --bot-cookies='[{"name":"consent","value":"granted","domain":".example.com"}]'

Playwright 集成

const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      '--bot-profile=/path/to/profile.enc',
      '--bot-cookies=@/path/to/cookies.json',
    ],
    headless: true,
  });

  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://example.com');

  // Cookies 在首次请求时已存在
  const cookies = await context.cookies();
  console.log('Active cookies:', cookies.length);
  await browser.close();
})();
async function createIdentity(profilePath, cookiesPath) {
  return chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      `--bot-profile=${profilePath}`,
      `--bot-cookies=@${cookiesPath}`,
    ],
    headless: true,
  });
}

const userA = await createIdentity('/profiles/user-a.enc', '/cookies/user-a.json');
const userB = await createIdentity('/profiles/user-b.enc', '/cookies/user-b.json');

在会话结束时保存 Cookie,然后在下次启动时重新加载:

// 在会话结束时
const cookies = await context.cookies();
const fs = require('fs');
fs.writeFileSync('/cookies/user-a.json', JSON.stringify(cookies, null, 2));

// 下次会话:使用 --bot-cookies=@/cookies/user-a.json 启动

验证

在使用 --bot-cookies 启动后验证 Cookie 是否正确加载:

const page = await context.newPage();

// 在任何导航之前检查 Cookie 是否存在
const cookies = await context.cookies('https://example.com');
console.log('Pre-loaded cookies:', cookies.map(c => c.name));

// 导航并验证请求中是否发送了 Cookie
await page.goto('https://example.com');

// 验证 JavaScript 可见的 Cookie
const jsCookies = await page.evaluate(() => document.cookie);
console.log('JS-visible cookies:', jsCookies);

你还可以使用框架的网络拦截功能检查初始请求头,确认首次请求中包含了 Cookie。

Cookie Loading Timeline --bot-cookies Engine init Context created Cookies ready First request Cookies sent addCookies() Too late --bot-cookies loads before context. Framework APIs load after.

最佳实践

  • 为每个身份保留独立的 Cookie 文件。 不要在不同的指纹配置文件之间共享同一 Cookie 文件。
  • 设置合理的到期时间。 与真实浏览器行为一致的远期到期时间更真实,避免使用过去的时间。
  • 包含常见的同意 Cookie。 GDPR 或同意令牌可以阻止每次访问都弹出同意横幅,使返回用户行为更自然。
  • 在轮换配置文件时同步轮换 Cookie。 更换指纹配置文件时,同时更新 Cookie 集以匹配新身份。
  • 正确使用 HttpOnly 和 Secure 标志。 按目标站点实际设置的 Set-Cookie 标志进行匹配。
  • 对文件路径使用 @ 前缀。 处理大量 Cookie 时,使用 @ 前缀可以让启动命令更简洁。

常见问题

该标志接受一个 Cookie 对象数组的 JSON。每个对象至少应包含 namevaluedomain。可选字段包括 pathexpirationDatesecurehttpOnlysameSite。可以内联传递 JSON,或用 @ 前缀引用文件。

通过 --bot-cookies 加载的 Cookie 在首次页面导航之前就可用。它们在浏览器初始化期间即已存在,因此即使是最先发出的 HTTP 请求也会携带这些 Cookie。

我可以将 --bot-cookies 与 Playwright 的 addCookies() 一起使用吗?

可以。--bot-cookies 会在初始化阶段首先加载 Cookie。你可以在需要时使用 context.addCookies() 添加更多 Cookie,两者是互补的。

在会话结束时使用自动化框架读取 Cookie(例如 Playwright 的 context.cookies()),保存为 JSON,然后在下次启动时将其传递给 --bot-cookies

--bot-cookies 是否适用于多个浏览器上下文?

通过 --bot-cookies 加载的 Cookie 可用于浏览器实例内的所有上下文。如果你需要为不同上下文使用不同的 Cookie,请在额外需要的 Cookie 上使用框架的每上下文 Cookie API。

哪个级别需要 --bot-cookies

--bot-cookies 标志为 PRO 级功能。

我可以将 --bot-cookies 与其他身份标志组合使用吗?

可以。将 --bot-cookies--bot-bookmarks--bot-inject-random-history、时区/语言相关标志等结合使用,可以创建在所有层面都一致的完整浏览器身份。

摘要

使用 --bot-cookies 进行的 Cookie 管理通过确保从首次请求开始就存在 Cookie 来完善 BotBrowser 的身份层。配合指纹配置文件和其他身份标志,它能创建在所有层面上保持一致状态的浏览器会话。

如需相关主题,请参见 Multi-Account IsolationProxy ConfigurationProfile Management

--bot-cookies 标志

BotBrowser 提供 --bot-cookies 标志(ENT Tier1)在启动时注入 cookie,让您在首次页面加载前完全控制会话状态。

./chrome \
  --bot-profile=/path/to/profile.enc \
  --bot-cookies=/path/to/cookies.json

cookie 文件使用标准 Chromium cookie 格式:

[
  {
    "domain": ".example.com",
    "path": "/",
    "name": "session_id",
    "value": "abc123def456",
    "expirationDate": 1774000000,
    "secure": true,
    "httpOnly": true,
    "sameSite": "Lax"
  },
  {
    "domain": ".example.com",
    "path": "/",
    "name": "user_pref",
    "value": "dark_mode=1&lang=en",
    "expirationDate": 1774000000,
    "secure": false,
    "httpOnly": false,
    "sameSite": "None"
  }
]

cookie 在浏览器启动时立即可用,在任何导航发生之前。

Playwright 示例

const { chromium } = require('playwright-core');

(async () => {
  const browser = await chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      '--bot-profile=/path/to/profile.enc',
      '--bot-cookies=/path/to/cookies.json',
    ],
    headless: true,
  });

  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://example.com');

  const cookies = await context.cookies();
  console.log('活跃 cookies:', cookies.length);
  await browser.close();
})();

--bot-cookies 标志在浏览器上下文完全初始化之前加载 cookie,使其在页面加载的最早阶段就可用。这与 Playwright 的 context.addCookies() 方法不同,后者在上下文创建后添加 cookie。

每个浏览器实例应携带自己的 cookie 集:

async function createIdentity(profilePath, cookiesPath) {
  return chromium.launch({
    executablePath: '/path/to/botbrowser/chrome',
    args: [
      `--bot-profile=${profilePath}`,
      `--bot-cookies=${cookiesPath}`,
    ],
    headless: true,
  });
}

const userA = await createIdentity('/profiles/user-a.enc', '/cookies/user-a.json');
const userB = await createIdentity('/profiles/user-b.enc', '/cookies/user-b.json');

每个实例运行完全独立的 cookie 存储。

在每个会话结束时保存 cookie,在下次启动时重新加载:

const cookies = await context.cookies();
const fs = require('fs');
fs.writeFileSync('/cookies/user-a.json', JSON.stringify(cookies, null, 2));

下次运行时,将保存的文件传递给 --bot-cookies,会话将从上次中断处继续。

最佳实践

  • 每个身份保持独立的 cookie 文件。 不要在不同的指纹配置之间共享 cookie 文件。
  • 设置合理的过期日期。 具有远期过期时间的 cookie 看起来很正常。
  • 包含常见的同意 cookie。 GDPR 或 cookie 同意令牌可防止每次访问都出现同意横幅。
  • 随配置轮换 cookie。 轮换指纹配置时,更新 cookie 集以匹配。
  • 正确使用 httpOnly 和 secure 标志。 匹配目标站点实际设置的标志。

后续步骤

#cookies#management#identity#session#privacy