browser_cookie3源码剖析
目录
-
概述 1.1 项目背景与定位 1.2 核心价值与应用场景 1.3 支持的浏览器矩阵 -
核心架构 2.1 整体架构与类继承关系 2.2 跨平台密钥获取策略 2.3 Cookie 加密方案总览 -
源码分析 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.1 各浏览器路径生成策略 4.2 Firefox profiles.ini 解析 4.3 会话 Cookie 加载 4.4 Chrome 24+ 完整性校验 -
技术亮点 5.1 三级数据库连接降级策略 5.2 多版本加密协议兼容 5.3 Linux 密钥获取的双通道设计 5.4 Windows Shadow Copy 支持 -
实践指南 6.1 快速上手 6.2 常见问题与排障 6.3 安全与合规注意事项 -
总结
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 支持的浏览器矩阵
|
|
|
|
|
|
|
|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2. 核心架构
2.1 整体架构与类继承关系
browser_cookie3 采用”基类 + 子类”的继承体系,按照浏览器内核分为三大支线:
ChromiumBased (Chromium 内核基类)├── Chrome├── Arc├── Chromium├── Opera├── OperaGX├── Brave├── Edge└── VivaldiFirefoxBased (Firefox 内核基类)├── Firefox└── LibreWolf独立实现├── Safari (二进制格式解析)├── Lynx (文本文件)└── W3m (文本文件)
每个浏览器子类的职责极为单一:向基类传递该浏览器的路径模板和密钥配置参数(如 os_crypt_name、osx_key_service 等)。所有核心逻辑(密钥获取、Cookie 解密、数据库读取)都在基类中实现。
2.2 跨平台密钥获取策略

流程执行说明:
-
Windows 平台(步骤 3-7):从浏览器 Local StateJSON 文件中提取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 加密方案总览
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
v10 |
|
|
|
|
|
v10 |
|
|
|
|
|
v10 |
|
peanuts |
|
|
|
v10
v11 |
|
|
|
|
|
|
|
|
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 个平台特定参数:
|
|
|
|
|---|---|---|
linux_cookies |
|
~/.config/google-chrome{channel}/Default/Cookies |
windows_cookies |
|
Google\Chrome\User Data\Default\Cookies |
osx_cookies |
|
~/Library/Application Support/Google/Chrome/... |
windows_keys |
|
Google\Chrome\User Data\Local State |
os_crypt_name |
|
chrome |
osx_key_service |
|
Chrome Safe Storage |
osx_key_user |
|
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 路径还会同时在 APPDATA、LOCALAPPDATA 和 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 实现:
-
dbusPython 包(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 |
|
|
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, QueryValueExroot = 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 |
|
|
sessionstore-backups/recovery.jsonlz4 |
|
|
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=ro、mode=ro&nolock=1、mode=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
|
dbus-python |
|
jeepney |
jeepney |
|
两个通道都实现了相同的密钥获取逻辑(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').nameshadowcopy.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
夜雨聆风