乐于分享
好东西不私藏

browser_cookie3源码剖析

browser_cookie3源码剖析

目录

  1. 概述  1.1 项目背景与定位  1.2 核心价值与应用场景  1.3 支持的浏览器矩阵
  2. 核心架构  2.1 整体架构与类继承关系  2.2 跨平台密钥获取策略  2.3 Cookie 加密方案总览
  3. 源码分析  3.1 ChromiumBased:Chromium 内核浏览器的核心实现  3.2 跨平台密钥获取详解  3.3 Cookie 解密流程  3.4 数据库连接策略  3.5 FirefoxBased:Firefox 内核浏览器的实现  3.6 Safari:二进制 Cookie 文件解析  3.7 CLI 入口:main.py
  4. 功能详解  4.1 各浏览器路径生成策略  4.2 Firefox profiles.ini 解析  4.3 会话 Cookie 加载  4.4 Chrome 24+ 完整性校验
  5. 技术亮点  5.1 三级数据库连接降级策略  5.2 多版本加密协议兼容  5.3 Linux 密钥获取的双通道设计  5.4 Windows Shadow Copy 支持
  6. 实践指南  6.1 快速上手  6.2 常见问题与排障  6.3 安全与合规注意事项
  7. 总结

1. 概述

1.1 项目背景与定位

browser_cookie3 是一个轻量级的 Python 库(PyPI 包名 browser-cookie3,当前版本 0.20.1),它能从本地浏览器的 Cookie 数据库中提取 Cookie,转换为标准的 http.cookiejar.CookieJar 对象,使 Python 程序可以”借用”浏览器已有的登录状态,直接访问需要认证的网页资源。

整个库只有一个核心源文件 __init__.py(1425 行)和一个 CLI 入口 __main__.py(62 行),代码精炼但覆盖面广——支持 Windows、macOS、Linux 三大平台,以及 Chrome、Firefox、Edge、Brave、Opera、Vivaldi、Arc、Safari、LibreWolf、Lynx、W3m 等 13 种浏览器。

1.2 核心价值与应用场景

核心价值:让 Python 程序无需手动登录,直接复用浏览器的认证状态。

典型应用场景:

  • 爬虫开发中复用浏览器登录态,避免处理验证码和多因素认证
  • 自动化测试中验证 Cookie 是否正确设置
  • 安全研究中分析浏览器 Cookie 存储机制
  • 个人工具脚本访问需要登录的 API(如社交媒体、知识星球等)

1.3 支持的浏览器矩阵

浏览器
内核
Windows
macOS
Linux
加密方式
Chrome
Chromium
Yes
Yes
Yes
AES-256-GCM / AES-128-CBC
Arc
Chromium
Yes
macOS Keychain
Chromium
Chromium
Yes
Yes
Yes
同 Chrome
Opera
Chromium
Yes
Yes
Yes
同 Chrome
Opera GX
Chromium
Yes
Yes
同 Chrome
Brave
Chromium
Yes
Yes
Yes
同 Chrome
Edge
Chromium
Yes
Yes
Yes
同 Chrome
Vivaldi
Chromium
Yes
Yes
Yes
同 Chrome
Firefox
Gecko
Yes
Yes
Yes
无加密(明文)
LibreWolf
Gecko
Yes
Yes
Yes
无加密
Safari
WebKit
Yes
二进制格式(无加密)
Lynx
文本
Yes
文本文件
W3m
文本
Yes
文本文件

2. 核心架构

2.1 整体架构与类继承关系

browser_cookie3 采用”基类 + 子类”的继承体系,按照浏览器内核分为三大支线:

ChromiumBased (Chromium 内核基类)├── Chrome├── Arc├── Chromium├── Opera├── OperaGX├── Brave├── Edge└── VivaldiFirefoxBased (Firefox 内核基类)├── Firefox└── LibreWolf独立实现├── Safari (二进制格式解析)├── Lynx (文本文件)└── W3m (文本文件)

每个浏览器子类的职责极为单一:向基类传递该浏览器的路径模板和密钥配置参数(如 os_crypt_nameosx_key_service 等)。所有核心逻辑(密钥获取、Cookie 解密、数据库读取)都在基类中实现。

2.2 跨平台密钥获取策略

流程执行说明:

  • Windows 平台(步骤 3-7):从浏览器 Local State JSON 文件中提取 os_crypt.encrypted_key 字段,经过 base64 解码后去除 5 字节 DPAPI 前缀,再通过 Windows DPAPI(CryptUnprotectData)解密,得到 AES-256-GCM 密钥
  • macOS 平台(步骤 8-10):调用系统命令 /usr/bin/security 从 Keychain 中获取浏览器的”Safe Storage”密码,通过 PBKDF2(1003 轮迭代)派生出 AES-128-CBC 密钥
  • Linux 平台(步骤 11-15):依次尝试 SecretService(libsecret)和 KDE Wallet 获取系统密钥,同时生成 v10(默认密码 peanuts)和 v11(系统密码)两套密钥,以及 v11_empty_key(空密码,用于兼容历史 bug)

2.3 Cookie 加密方案总览

平台
Chrome 版本
加密前缀
算法
密钥来源
Windows
< 80
DPAPI
Windows 用户凭据
Windows
>= 80
v10
AES-256-GCM
Local State + DPAPI
macOS
全版本
v10
AES-128-CBC
Keychain + PBKDF2
Linux
< 某版本
v10
AES-128-CBC
默认密码 peanuts
Linux
>= 某版本
v10

/v11
AES-128-CBC
SecretService/KDE Wallet + PBKDF2
Firefox
全版本
无加密
明文存储

3. 源码分析

3.1 ChromiumBased:Chromium 内核浏览器的核心实现

ChromiumBased 是整个库最核心的类(第 431-647 行),承担密钥管理、Cookie 解密和数据库读取三大职责。

初始化参数体系:

classChromiumBased:    UNIX_TO_NT_EPOCH_OFFSET = 11644473600# NT epoch to Unix epoch offsetdef__init__(self, browser, cookie_file=None, domain_name="", key_file=None, **kwargs):        self.salt = b'saltysalt'# Chromium 统一盐值   self.iv=b' '*16# 16 字节空格 IV(AES-128-CBC)   self.length=16# 密钥长度        self.browser = browser# ... 传递给 __add_key_and_cookie_file 处理平台差异

每个子类通过 **args 字典传递 7 个平台特定参数:

参数
作用
示例(Chrome)
linux_cookies
Linux Cookie 文件路径模板
~/.config/google-chrome{channel}/Default/Cookies
windows_cookies
Windows Cookie 文件路径
Google\Chrome\User Data\Default\Cookies
osx_cookies
macOS Cookie 文件路径
~/Library/Application Support/Google/Chrome/...
windows_keys
Windows 加密密钥文件路径
Google\Chrome\User Data\Local State
os_crypt_name
Linux 密钥管理器中的应用名
chrome
osx_key_service
macOS Keychain 服务名
Chrome Safe Storage
osx_key_user
macOS Keychain 用户名
Chrome

路径模板引擎:

路径生成通过 _genarate_nix_paths_chromium() 和 _genarate_win_paths_chromium() 实现,支持 {channel} 占位符扩展:

# Chrome 的 channel 参数覆盖了稳定版、Beta 和 Dev 三个渠道channel=[''' Beta'' Dev']# 展开后为:# Google\Chrome\User Data\Default\Cookies# Google\Chrome Beta\User Data\Default\Cookies# Google\Chrome Dev\User Data\Default\Cookies

Windows 路径还会同时在 APPDATALOCALAPPDATA 和 APPDATA\..\Local 三个位置搜索,确保覆盖不同安装位置。

3.2 跨平台密钥获取详解

Windows:DPAPI + Local State JSON

# __init__.py:475-489 (Windows 分支)key_file = self.key_file or _expand_paths(windows_keys, 'windows')if key_file:withopen(key_file, 'rb'as f:        key_file_json = json.load(f)        key64 = key_file_json['os_crypt']['encrypted_key'].encode('utf-8')        keydpapi = base64.standard_b64decode(key64)[5:]  # 去除 'DPAPI' 前缀        _, self.v10_key = _crypt_unprotect_data(keydpapi, is_key=True)

关键细节:encrypted_key 字段经 base64 解码后,前 5 字节是 b'DPAPI' 文本前缀,需去除后才能传给 CryptUnprotectData。这个函数通过 ctypes 直接调用 Windows crypt32.dll

# __init__.py:72-108classDataBlob(ctypes.Structure):    _fields_ = [        ('cbData', ctypes.wintypes.DWORD),        ('pbData', ctypes.POINTER(ctypes.c_char))    ]ctypes.windll.crypt32.CryptUnprotectData(    ctypes.byref(blob_in), ctypes.byref(desc),    ctypes.byref(blob_entropy), reserved, prompt_struct,    CRYPTPROTECT_UI_FORBIDDEN, ctypes.byref(blob_out))

macOS:Keychain + PBKDF2

# __init__.py:111-121def_get_osx_keychain_password(osx_key_service, osx_key_user):    cmd = ['/usr/bin/security''-q''find-generic-password','-w''-a', osx_key_user, '-s', osx_key_service]    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)    out, err = proc.communicate()if proc.returncode != 0:return CHROMIUM_DEFAULT_PASSWORD  # fallback: b'peanuts'return out.strip()

macOS 的密钥派生使用 PBKDF2,迭代次数 1003 次(这是 Chromium 的固定值),盐值为 b'saltysalt',输出 16 字节 AES 密钥。

Linux:SecretService / KDE Wallet 双通道

Linux 的密钥获取最为复杂,通过 _LinuxPasswordManager 类实现,支持两种 DBus 实现:

  • dbus Python 包(USE_DBUS_LINUX=True
  • jeepney 纯 Python DBus 实现(USE_DBUS_LINUX=False

密码获取策略为三级降级:

# __init__.py:240-250defget_password(self, os_crypt_name):try:return self.__get_secretstorage_password(os_crypt_name)  # 优先 SecretServiceexcept RuntimeError:passtry:return self.__methods_map.get('kwallet')(os_crypt_name)  # 其次 KDE Walletexcept RuntimeError:passreturn CHROMIUM_DEFAULT_PASSWORD  # 最后回退到默认密码

SecretService 查询时会依次尝试两个 Schema 版本(v2 和 v1),对应 Chromium 不同时期的密钥存储格式。

3.3 Cookie 解密流程

_decrypt() 方法(第 591-646 行)是整个库的解密核心,需要处理多种加密方案:

流程执行说明:

  • 步骤 1-2:如果 value 字段非空,说明 Cookie 值本身未被加密,直接返回
  • 步骤 3-5:Windows 平台首先尝试 DPAPI 解密(Chrome 80 之前的方式)
  • 步骤 6-8:DPAPI 失败后,识别为 Chrome 80+ 的 AES-256-GCM 加密。去除 3 字节 v10 前缀,提取 12 字节 nonce 和 16 字节 GCM tag,用 v10_key 解密
  • 步骤 9-14:macOS/Linux 平台根据 v10/v11 前缀选择对应的密钥,使用 AES-128-CBC 解密
  • 步骤 13-14:Linux 的 v11 加密会依次尝试系统密钥和空密钥(兼容 Chromium 历史 bug)

Chrome 24+ 完整性校验处理:

# __init__.py:616-617 (Windows) 和 641-642 (macOS/Linux)if has_integrity_check_for_cookie_domain:    data = data[32:]  # 去除 32 字节的 SHA256 domain hash 前缀

从 Chrome 24 开始,加密 Cookie 值前会添加 32 字节的域名 SHA256 哈希作为完整性校验。库通过查询 Cookie 数据库 meta 表的 version 字段判断是否需要跳过这 32 字节。

3.4 数据库连接策略

_DatabaseConnetion 类(第 346-428 行,注意拼写错误保留自原代码)实现了三级降级策略,确保在浏览器锁定数据库文件时仍能读取:

class_DatabaseConnetion:def__init__(self, database_file, try_legacy_first=False):        self.__methods = [            self.__sqlite3_connect_readonly,  # 优先:只读 URI 模式        ]if try_legacy_first:            self.__methods.insert(0, self.__get_connection_legacy)else:            self.__methods.append(self.__get_connection_legacy)if shadowcopy:            self.__methods.append(self.__get_connection_shadowcopy)
策略
原理
适用场景
sqlite3_connect_readonly
使用 file://URI?mode=ro 只读打开,依次尝试 ?mode=ro?mode=ro&nolock=1?mode=ro&immutable=1
浏览器未运行时
get_connection_legacy
将数据库文件复制到临时文件,对副本打开连接
浏览器运行中(文件被锁定)
get_connection_shadowcopy
通过 Windows VSS(卷影复制)创建文件快照
Windows + 文件被独占锁定

Firefox 使用 try_legacy_first=True,优先使用复制策略,因为 Firefox 的 SQLite 锁定机制更严格。

3.5 FirefoxBased:Firefox 内核浏览器的实现

Firefox 系列(第 926-1069 行)与 Chromium 系列的关键区别:Firefox 不加密 Cookie 值,直接以明文存储在 SQLite 数据库中。

profiles.ini 解析:

# __init__.py:943-971@staticmethoddefget_default_profile(user_data_path):    config = configparser.ConfigParser()    profiles_ini_path = glob.glob(os.path.join(user_data_path + '**''profiles.ini'))# ...for section in config.sections():ifsection.startswith('Install'): # Install 段优先级最高            profile_path = config[section].get('Default')breakelif config[section].get('Default') == '1':  # 其次是 Default=1 的段            profile_path = config[section].get('Path')

Firefox 的多 Profile 机制通过 profiles.ini 配置文件管理。库解析时优先查找 Install 开头的段(Firefox 72.0.1+ 的优先级规则),然后是标记 Default=1 的段,最后处理相对/绝对路径。

会话 Cookie 加载:

Firefox 的会话 Cookie 不在 SQLite 数据库中,而是保存在 sessionstore.js(JSON 明文)或 sessionstore-backups/recovery.jsonlz4(LZ4 压缩的 JSON)中:

# __init__.py:1032-1045 (LZ4 格式)def__add_session_cookies_lz4(self, cj):withopen(self.session_file_lz4, 'rb'as file_obj:        file_obj.read(8)  # 跳过 8 字节 mozLz4 魔数头        json_data = json.loads(lz4.block.decompress(file_obj.read()))for cookie in json_data.get('cookies', []):ifself.domain_name==''orself.domain_nameincookie.get('host',''):            cj.set_cookie(FirefoxBased.__create_session_cookie(cookie))

3.6 Safari:二进制 Cookie 文件解析

Safari 使用自定义的二进制格式 Cookies.binarycookies,需要手动解析二进制结构(第 1112-1232 行):

文件结构:[4 bytes: 'cook' magic][4 bytes: page count][page_count * 4 bytes: page sizes][page 1 data][page 2 data] ... [page N data]每页结构:[4 bytes: 0x00000100 magic][4 bytes: cookie count][cookie_count * 4 bytes: cookie offsets][4 bytes: 0x00000000 terminator][cookie data ...]每条 Cookie:[4 bytes: size][4 bytes: unknown][4 bytes: flags][4 bytes: unknown][4 bytes: host_offset][4 bytes: name_offset][4 bytes: path_offset][4 bytes: value_offset][4 bytes: comment_offset][4 bytes: 0x00000000][8 bytes: expiry_date (Apple epoch)][8 bytes: creation_date (Apple epoch)][null-terminated strings: host, name, path, value, comment]

Safari 的时间戳使用 Apple epoch(2001-01-01 基准),需要加上 978307200 秒偏移量转换为 Unix epoch。

3.7 CLI 入口:main.py

# __main__.py:38-58defmain(args=None):    p, args = parse_args(args)if args.browser:        cj = args.browser(cookie_file=args.cookie_file, key_file=args.key_file)else:        cj = browser_cookie3.load()  # 尝试所有浏览器for cookie in cj:ifcookie.domainin(args.domain,'.'+args.domain)andcookie.name== args.name:ifnot args.json:print(cookie.value)else:print(json.dumps({k:vfork,vinvars(cookie).items()ifvisnotNoneand(k,v)!=('_rest', {})}))breakelse:raise SystemExit(1)  # 未找到 Cookie

CLI 支持所有浏览器的命令行参数(如 --chrome--firefox),以及 --json 输出完整 Cookie 详情。退出码:0 表示找到,1 表示未找到,2 表示错误。

4. 功能详解

4.1 各浏览器路径生成策略

Chromium 内核浏览器的路径生成采用模板 + 渠道参数的方式。以 Chrome 为例:

Windows 路径搜索优先级(由 _genarate_win_paths_chromium 生成):

1. %LOCALAPPDATA%\Google\Chrome\UserData\Default\Cookies2. %LOCALAPPDATA%\Google\Chrome\UserData\Default\Network\Cookies3. %LOCALAPPDATA%\Google\Chrome\UserData\Profile *\Cookies4. %LOCALAPPDATA%\Google\Chrome\UserData\Profile *\Network\Cookies5. %APPDATA%\..\Local\Google\Chrome\UserData\... (同上 4 个)6. %APPDATA%\Google\Chrome\UserData\... (同上 4 个)

共 12 个候选路径 × 3 个渠道(Stable/Beta/Dev)= 36 个路径。_expand_paths() 返回第一个匹配的路径。

Group Policy 支持:

Windows 上还通过注册表检查 Group Policy 配置的 UserDataDir

# __init__.py:54-68def_windows_group_policy_path():from winreg import ConnectRegistry, OpenKeyEx, QueryValueEx    root = ConnectRegistry(None, HKEY_LOCAL_MACHINE)    policy_key = OpenKeyEx(root, r"SOFTWARE\Policies\Google\Chrome")    user_data_dir, type_ = QueryValueEx(policy_key, "UserDataDir")return os.path.join(user_data_dir, "Default""Cookies")

这确保了在企业环境中通过组策略定制了 Chrome 数据目录时也能正确找到 Cookie 文件。

4.2 Firefox profiles.ini 解析

Firefox 的多 Profile 支持通过 profiles.ini 文件管理,解析逻辑需要处理多种边缘情况:

  • Install 段优先级高于 Default=1 段(Firefox 72.0.1+ 规则)
  • IsRelative 字段决定路径是相对于 profiles.ini 所在目录还是绝对路径
  • Snap 包安装的 Firefox 数据路径为 ~/snap/firefox/common/.mozilla/firefox
  • 如果没有 profiles.ini,退回到用户数据目录的通配符搜索

4.3 会话 Cookie 加载

会话 Cookie(Session Cookie)的加载是 browser_cookie3 的一个重要特性。Firefox 的会话 Cookie 保存在两个位置:

文件
格式
说明
sessionstore.js
JSON 明文
主会话存储文件
sessionstore-backups/recovery.jsonlz4
LZ4 压缩
会话恢复备份

LZ4 格式需要跳过前 8 字节的 mozLz4 魔数头,然后使用 lz4.block.decompress() 解压。

Chromium 内核浏览器的会话 Cookie 直接存储在 SQLite 数据库中(expires_utc=0),不需要额外处理。

4.4 Chrome 24+ 完整性校验

从 Chrome 24 版本开始,加密 Cookie 值前会添加 32 字节的域名 SHA256 哈希:

# __init__.py:556-576@staticmethoddef_has_integrity_check_for_cookie_domain(con):try:        value, = con.execute('SELECT value FROM meta WHERE key = "version";').fetchone()except sqlite3.OperationalError:returnFalsereturnint(value) >= 24

通过查询 Cookie 数据库 meta 表的 version 值判断。当 version >= 24 时,解密后的数据需要跳过前 32 字节的 domain hash,才能得到实际的 Cookie 值。

5. 技术亮点

5.1 三级数据库连接降级策略

浏览器运行时会锁定其 SQLite 数据库文件,直接打开会报错。browser_cookie3 通过三级降级策略解决这个问题:

  • 第一级:SQLite URI 只读模式 — 尝试三种不同的只读参数组合(mode=romode=ro&nolock=1mode=ro&immutable=1),在浏览器未运行时最快
  • 第二级:文件复制 — 将整个数据库文件复制到临时目录后打开副本,绕过文件锁。这是最通用的方案,Firefox 默认使用此策略
  • 第三级:Shadow Copy — 仅 Windows 平台,通过 shadowcopy 库调用 VSS(Volume Shadow Copy Service)创建文件快照,适用于文件被独占锁定的场景
# __init__.py:376-384 (只读模式)for options in ('?mode=ro''?mode=ro&nolock=1''?mode=ro&immutable=1'):try:        con = sqlite3.connect(uri + options, uri=True)except sqlite3.OperationalError:continueif self.__check_connection_ok(con):return con

5.2 多版本加密协议兼容

_decrypt() 方法需要兼容多种加密方案,这是代码最复杂的部分:

  • Chrome < 80(Windows):使用 DPAPI 直接加密,无前缀
  • Chrome >= 80(Windows):使用 AES-256-GCM,v10 前缀,密钥通过 DPAPI 解密 Local State 获得
  • macOS(全版本):使用 AES-128-CBC,v10 前缀,密钥通过 Keychain + PBKDF2 派生
  • Linux(早期):使用 AES-128-CBC,v10 前缀,默认密码 peanuts
  • Linux(后期):使用 AES-128-CBC,v10 或 v11 前缀,密钥来自 SecretService 或 KDE Wallet
  • Linux(bug 兼容)v11 前缀但密钥为空字符串(Chromium 历史 bug 导致)

Linux 平台的解密会依次尝试 v11_key → v11_empty_key,确保覆盖所有历史情况。

5.3 Linux 密钥获取的双通道设计

Linux 平台的密钥获取通过 _LinuxPasswordManager 类实现了 DBus 双通道:

通道
依赖包
适用场景
dbus

 (Python-DBus)
dbus-python
GNOME 桌面环境
jeepney jeepney
纯 Python 实现,无 C 依赖

两个通道都实现了相同的密钥获取逻辑(SecretService 和 KDE Wallet),区别仅在于 DBus 调用方式。优先使用 jeepney(纯 Python,兼容性更好),dbus-python 作为备选。

5.4 Windows Shadow Copy 支持

当浏览器独占锁定 Cookie 数据库文件时,即使文件复制也会失败。shadowcopy 库通过 Windows VSS 创建文件快照,可以绕过文件锁:

# __init__.py:397-406def__get_connection_shadowcopy(self):    self.__temp_cookie_file = tempfile.NamedTemporaryFile(suffix='.sqlite').name    shadowcopy.shadow_copy(self.__database_file, self.__temp_cookie_file)    con = sqlite3.connect(self.__temp_cookie_file)if self.__check_connection_ok(con):return con

shadowcopy 是可选依赖(仅在 Windows 上导入),不存在时跳过此策略。

6. 实践指南

6.1 快速上手

安装:

pip install browser-cookie3

基本用法:

import browser_cookie3import requests# 从 Chrome 获取指定域名的 Cookiecj = browser_cookie3.chrome(domain_name='.example.com')r = requests.get('https://example.com/api/data', cookies=cj)print(r.json())# 从所有浏览器加载 Cookiecj = browser_cookie3.load(domain_name='.github.com')# 指定浏览器cj = browser_cookie3.firefox(domain_name='.example.com')cj = browser_cookie3.edge(domain_name='.example.com')cj = browser_cookie3.brave(domain_name='.example.com')

CLI 用法:

# 获取 GitHub 的 _gh_sess Cookie 值browser-cookie github.com _gh_sess# 从 Chrome 获取,输出 JSON 格式browser-cookie --chrome -j github.com _gh_sess# 尝试所有浏览器browser-cookie -a example.com session_id

6.2 常见问题与排障

“Failed to find cookies” 错误:

  • 确认目标浏览器已安装并运行过
  • 检查浏览器数据目录是否在默认位置
  • Windows 上检查 %LOCALAPPDATA% 下对应浏览器目录是否存在

“Unable to get key for cookie decryption” 错误:

  • macOS:确认 Keychain 中存在对应浏览器的 “Safe Storage” 条目
  • Linux:确认 libsecret 或 kwallet 服务正在运行
  • Windows:Local State 文件可能损坏,尝试关闭 Chrome 后重试

“Unable to read database file” 错误:

  • 浏览器正在运行导致文件被锁定
  • 安装 shadowcopy 依赖(Windows):pip install shadowcopy

Cookie 值解密后为空或乱码:

  • 可能是 Chrome 版本更新导致加密方式变化
  • 尝试指定 key_file 参数手动指向 Local State 文件

6.3 安全与合规注意事项

browser_cookie3 能读取浏览器 Cookie,这意味着它也能获取用户的登录凭证。使用时需注意:

  • 仅在授权范围内使用,不要读取不相关的域名 Cookie
  • 不要将获取的 Cookie 值写入日志或持久化存储
  • 在生产环境中,优先使用 OAuth 等标准授权流程
  • 部分网站的安全策略会检测异常的 Cookie 使用模式

7. 总结

browser_cookie3 以 1425 行代码实现了一个跨平台、多浏览器的 Cookie 提取库,其设计体现了几个值得学习的工程实践:

  • 基类抽象:通过 ChromiumBased 和 FirefoxBased 两个基类,将核心逻辑(密钥获取、解密、数据库读取)收敛,子类仅负责路径配置,实现了高度的代码复用
  • 多级降级策略:从数据库连接(只读 → 复制 → Shadow Copy)到密钥获取(SecretService → KDE Wallet → 默认密码),每一步都有备选方案
  • 加密协议全版本兼容:从 Chrome < 80 的 DPAPI 到 Chrome >= 80 的 AES-256-GCM,从 macOS 的 PBKDF2 到 Linux 的多密钥体系,覆盖了 Chromium 加密方式的全部历史演变
  • 跨平台密钥管理:Windows DPAPI、macOS Keychain、Linux SecretService/KDE Wallet 三套密钥获取方案,统一为相同的 AES 密钥接口

局限性:库的最后一次主要更新停留在对现有浏览器的支持,对于 Chrome 120+ 引入的 App-Bound Encryption(Windows 上使用系统级服务保护密钥)尚不支持。对于这一场景,需要考虑 pycookiecheat 等替代方案。

适用人群:需要自动化访问需要登录态的网页资源的 Python 开发者、安全研究人员、爬虫工程师。

参考文献

[1] browser_cookie3 GitHub 仓库:https://github.com/borisbabic/browser_cookie3

[2] Chromium Cookie 加密实现:https://chromium.googlesource.com/chromium/src/components/os_crypt/

[3] Windows DPAPI 文档:https://learn.microsoft.com/en-us/windows/win32/api/dpapi/

[4] Chromium Local State 密钥格式:https://chromium.googlesource.com/chromium/src/components/os_crypt/os_crypt_win.cc

[5] Chrome 24+ Cookie 完整性校验:https://issues.chromium.org/issues/40185252

[6] Firefox profiles.ini 格式:https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data

[7] pycookiecheat(替代方案):https://github.com/n8henrie/pycookiecheat