资讯动态

HackBrowserData 的 Firefox 数据存储结构解析:Profile 目录、数据文件布局与提取原理

发布时间:2026/10/4 8:51:41 来源:尧图企业网站定制
网络安全应用安全密码学CLI【免费下载链接】HackBrowserDataExtract and decrypt browser data, supporting multiple data types, runnable on various operating systems (macOS, Windows, Linux).项目地址https://gitcode.com/gh_mirrors/ha/HackBrowserData点击查看免费下载导读本文是 HackBrowserData 项目中 RFC-004: Firefox Data Storage 的深度解读围绕 Firefox 在本地磁盘上的数据存储布局展开从 Profile 目录的发现机制、七大数据类型的文件位置与内部格式到各类时间戳的换算规则以及与 Chromium 系浏览器存储方案的差异。读完本文你将掌握 Firefox 的密码、Cookie、历史记录、下载、书签、扩展与 LocalStorage 各自存在哪里、长什么样、如何解析并能对照 HackBrowserData 的 Go 源码browser/firefox/目录理解每条 SQL 与 JSON 解析逻辑的实际落地方式。1. Profile 目录结构随机前缀命名的用户数据根Firefox 与 Chromium 最大的结构差异体现在 Profile 的组织方式上。Firefox 将每个用户的数据保存在独立的profile 目录中这些目录位于平台相关的根目录之下例如 macOS 的~/Library/Application Support/Firefox/Profiles/、Windows 的%APPDATA%\Mozilla\Firefox\Profiles\以及 Linux 的~/.mozilla/firefox/。每个 profile 目录采用随机前缀 固定后缀的命名方式例如97nszz88.default-release前缀97nszz88是随机生成的 8 位字符后缀default-release表示配置类型。正是这种命名方式让按名字找 Profile的方案失效——HackBrowserData 必须通过内容探测来发现 Profile。Profile 发现的两种策略对比RFC-004 明确指出Firefox 的 Profile 校验与 Chromium 完全不同Chromium寻找名为Preferences的哨兵文件sentinel file来判断目录是否为有效 ProfileFirefox只要目录中包含任意一个已知数据文件如logins.json、cookies.sqlite、places.sqlite、extensions.json、webappsstore.sqlite即视为有效 Profile。这一逻辑在源码中得到了完整实现。在 browser/firefox/firefox.go 中discoverProfiles枚举用户数据根目录下的所有子目录hasAnySource遍历firefoxSources表检查是否存在至少一个已知数据源文件// discoverProfiles lists subdirectories of userDataDir that contain at least // one known data source. Each such directory is a Firefox profile. func discoverProfiles(userDataDir string, sources map[types.Category][]sourcePath) []string { entries, err : os.ReadDir(userDataDir) if err ! nil { return nil } var profiles []string for _, e : range entries { if !e.IsDir() { continue } dir : filepath.Join(userDataDir, e.Name()) if hasAnySource(sources, dir) { profiles append(profiles, dir) } } return profiles }而每个 Profile 是独立的数据提取单元——从源码结构看profile结构体browser/firefox/profile.go持有自己的目录路径、浏览器名和解析后的源文件映射与 Chromium 不同每个 Firefox Profile 拥有独立的主密钥来源于各自的key4.db因此提取时必须逐 Profile 派生密钥。2. 数据文件位置七类数据、五类源文件RFC-004 给出了完整的文件位置表。以下路径均相对于 Profile 目录类别Category文件格式密码Passwordlogins.jsonJSONCookiecookies.sqliteSQLite历史记录Historyplaces.sqliteSQLite下载Downloadplaces.sqliteSQLite书签Bookmarkplaces.sqliteSQLite扩展Extensionextensions.jsonJSONLocalStoragewebappsstore.sqliteSQLite关键结论RFC-004 明确强调History、Download、Bookmark 共用同一个places.sqlite只是查询其中不同的表Firefox 不支持 CreditCard信用卡与 SessionStorage 的提取主加密密钥独立存放在key4.db中其派生与解密逻辑见 RFC-005: Firefox Encryption。上述映射在 browser/firefox/source.go 中以firefoxSources表的形式直接落地且每个类别支持多个候选路径按优先级尝试首个存在的路径生效var firefoxSources map[types.Category][]sourcePath{ types.Password: {file(logins.json)}, types.Cookie: {file(cookies.sqlite)}, types.History: {file(places.sqlite)}, types.Download: {file(places.sqlite)}, types.Bookmark: {file(places.sqlite)}, types.Extension: {file(extensions.json)}, types.LocalStorage: {file(webappsstore.sqlite)}, }值得注意的实现细节profile.extractbrowser/firefox/profile.go在执行真正的解析前会通过filemanager.NewSession()创建临时会话把 Profile 内的源文件复制到临时目录后再读取避免与正在运行的 Firefox 实例产生文件占用冲突随后获取key4.db派生主密钥若已获取logins.json还会用真实登录数据验证密钥候选是否有效最后按类别逐个调用对应的extractXxx解析函数。3. 各数据类型的具体存储格式与解析3.1 密码logins.json—— 唯一加密的数据类型Firefox 的密码以 JSON 文件存储顶层是一个logins数组每个条目包含formSubmitURL/hostname—— 登录 URL优先取formSubmitURL为空时回退到hostnameencryptedUsername—— base64 编码、经 ASN1 PBE 加密的用户名encryptedPassword—— base64 编码、经 ASN1 PBE 加密的密码timeCreated—— 创建时间戳单位是毫秒milliseconds。解密流水线RFC-004base64 解码 → 解析为 ASN1 PBE 结构 → 使用主密钥解密。该流程在 browser/firefox/extract_password.go 中被封装为一次性的decryptPBE调用并配合gjson遍历logins数组// decryptPBE combines base64 decode ASN1 PBE parse decrypt into one call. func decryptPBE(encoded string, masterKey []byte) ([]byte, error) { raw, err : base64.StdEncoding.DecodeString(encoded) if err ! nil { return nil, fmt.Errorf(base64 decode: %w, err) } pbe, err : crypto.NewASN1PBE(raw) if err ! nil { return nil, fmt.Errorf(parse asn1 pbe: %w, err) } plaintext, err : pbe.Decrypt(masterKey) if err ! nil { return nil, fmt.Errorf(decrypt: %w, err) } return plaintext, nil }主密钥本身来自 Profile 的key4.db在 browser/firefox/firefox.go 的retrieveMasterKey中通过readKey4DBderiveKeys派生候选密钥并在存在logins.json时用validateKeyWithLogins校验——只有当某个候选密钥能真实解密登录条目时才被采用否则报错derived N key(s) but none could decrypt logins。3.2 Cookiecookies.sqlite—— 明文存储无需解密RFC-004 特别强调Firefox 的 Cookie 不加密值以明文存储这与 Chromium 用主密钥加密 Cookie 的做法截然不同。查询 SQLSELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies另一个实操要点数据库必须以journal_modeoff打开以避免与正在运行的 Firefox 实例产生锁冲突。源码中的实际查询browser/firefox/extract_cookie.go在 RFC 基础上还多取了sameSite字段const ( firefoxCookieQuery SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly, sameSite FROM moz_cookies firefoxCountCookieQuery SELECT COUNT(*) FROM moz_cookies )journal_modeoff的设置在 SQLite 工具层统一实现——utils/sqliteutil/query.go 与 utils/sqliteutil/sqlite.go 中均有db.Exec(PRAGMA journal_modeoff)。此外从源码可以看出sameSite字段的取值映射0→none、1→lax、2→strict、256表示未由 Set-Cookie 指定保持默认unspecified。3.3 历史记录places.sqlite—— 共用库的第一张表History 查询moz_places表SELECT url, COALESCE(last_visit_date, 0), COALESCE(title, ), visit_count FROM moz_places其中last_visit_date列使用**微秒microseconds**时间戳自 Unix 纪元起。源码实现browser/firefox/extract_history.go与 RFC 完全一致同时用COALESCE处理NULL的last_visit_date与title将历史记录解析为URL、Title、VisitCount、LastVisit字段并按VisitCount升序排序。3.4 下载记录places.sqlite—— 注解表 JOIN 主表Downloads 使用moz_annos注解表与moz_places连接SELECT place_id, GROUP_CONCAT(content), url, dateAdded FROM (SELECT * FROM moz_annos INNER JOIN moz_places ON moz_annos.place_id moz_places.id) t GROUP BY place_id下载元数据被存储为拼接字符串target_path,{json}其中 JSON 部分包含fileSize和endTime两个字段。源码解析逻辑browser/firefox/extract_download.go使用strings.SplitN(content, ,{, 2)拆分该字符串前半段为TargetPath后半段补回{后用gjson读取fileSize文件总字节数与endTime结束时间毫秒。这与 RFC 的描述一一对应。3.5 书签places.sqlite—— type 字段区分 URL 与文件夹Bookmarks 查询SELECT id, url, type, dateAdded, COALESCE(title, ) FROM (SELECT * FROM moz_bookmarks INNER JOIN moz_places ON moz_bookmarks.fk moz_places.id)type字段用于区分 URL 书签值为1与文件夹。源码中的bookmarkType函数browser/firefox/extract_bookmark.go正是 RFC 描述的落地type 1返回url其余一律视为folder。3.6 扩展extensions.json—— 仅收录用户级扩展扩展信息从addons数组读取只包含location app-profile的条目即用户安装的扩展而非系统内置。提取字段包括defaultLocale.name—— 名称id—— 扩展 IDversion—— 版本defaultLocale.description—— 描述defaultLocale.homepageURL—— 主页 URLactive—— 是否启用源码browser/firefox/extract_extension.go用gjson遍历addons数组并过滤locationfor _, v : range gjson.GetBytes(data, addons).Array() { // Only include user-installed extensions if v.Get(location).String() ! app-profile { continue } extensions append(extensions, types.ExtensionEntry{ Name: v.Get(defaultLocale.name).String(), ID: v.Get(id).String(), Description: v.Get(defaultLocale.description).String(), Version: v.Get(version).String(), HomepageURL: v.Get(defaultLocale.homepageURL).String(), Enabled: v.Get(active).Bool(), }) }3.7 LocalStoragewebappsstore.sqlite—— 反转域名格式LocalStorage 查询SELECT originKey, key, value FROM webappsstore2originKey列使用反转主机名格式例如moc.buhtig.:https:443代表https://github.com:443。其构成规则为主机部分按字节反转并附加点后缀剩余字段分别是 scheme协议和端口。源码parseOriginKeybrowser/firefox/extract_storage.go实现了这一逆解析按:拆分为三段对主机部分做字节级反转reverseString去掉点前缀后重组为scheme://host:port形式// parseOriginKey converts Firefoxs reversed origin format to a URL. // Example: moc.buhtig.:https:443 → https://github.com:443 func parseOriginKey(originKey string) string { parts : strings.SplitN(originKey, :, 3) if len(parts) 2 { return originKey } host : reverseString(parts[0]) host strings.TrimPrefix(host, .) scheme : parts[1] if len(parts) 3 { return fmt.Sprintf(%s://%s:%s, scheme, host, parts[2]) } return fmt.Sprintf(%s://%s, scheme, host) }4. 时间格式Firefox 不统一的时间戳单位RFC-004 用一个专门的章节强调Firefox 在不同数据类型之间使用了不一致的时间戳单位但都基于 Unix 纪元。这一点是解析时最容易踩坑的地方转换规则如下数据类型字段单位换算为秒/时间CookiecreationTime微秒÷ 1,000,000Cookieexpiry秒直接使用历史记录last_visit_date微秒÷ 1,000,000下载dateAdded微秒÷ 1,000,000下载endTime毫秒÷ 1,000书签dateAdded微秒÷ 1,000,000密码timeCreated毫秒÷ 1,000该规则在 browser/firefox/firefox.go 中封装为三个辅助函数注释里明确区分了三种单位firefoxMicrosPRTime即自 Unix 纪元起的微秒用于moz_*系列表Cookie 的creationTime、历史与书签的dateAddedfirefoxMillisDate.now()产生的毫秒用于logins.json与下载记录的endTimefirefoxSeconds秒仅用于moz_cookies.expiry。// - firefoxMicros: PRTime (μs since Unix epoch) — moz_* tables. // - firefoxMillis: Date.now() (ms) — logins.json, download endTime. // - firefoxSeconds: seconds — moz_cookies.expiry only. func firefoxMicros(us int64) time.Time { if us 0 { return time.Time{} } return clampJSON(time.UnixMicro(us).UTC()) }从源码还可以看到一个健壮性细节clampJSONbrowser/firefox/firefox.go会将超出time.Time.MarshalJSON可表示年份区间19999的时间映射为零值从而保证 JSON 导出不会因哨兵值或异常时间戳而崩溃非正数输入同样返回零值time.Time{}。5. 与 Chromium 存储方案的关键差异RFC-004 最后给出了一张对照表从数据存储角度总结 Firefox 与 Chromium 系的六大差异维度ChromiumFirefoxProfile 命名具名目录Default、Profile 1随机前缀97nszz88.default-releaseProfile 检测Preferences哨兵文件任一已知源文件存在即可密码存储SQLiteLogin DataJSONlogins.jsonCookie 加密使用主密钥加密明文共享数据库各类别独立文件History/Download/Bookmark 共用places.sqliteLocalStorageLevelDBSQLitewebappsstore.sqlite信用卡CreditCard支持支持不支持SessionStorage 支持支持不支持加密范围密码、Cookie、信用卡仅密码参见 RFC-005这些差异直接决定了提取器的架构走向HackBrowserData 为 Firefox 单独实现了browser/firefox/包而非复用 Chromium 的提取逻辑。从 browser/firefox/profile.go 的extractCategory可以看到CreditCard 与 SessionStorage 两个类别在 Firefox 侧被显式跳过case types.CreditCard, types.SessionStorage: // Firefox does not support CreditCard or SessionStorage extraction.另外browser/firefox/firefox.go 的注释还点明了一个加密架构层面的差异由于 Firefox 的密钥是per-profile每个 Profile 的key4.db各自独立整个安装级Browser并不实现统一的 KeyManager 接口密钥派生必须下放到单个 Profile 的提取流程中完成。6. 关联 RFC 与进一步阅读Firefox 数据存储只是 HackBrowserData 逆向工程文档体系的一部分与其直接相关的 RFC 包括RFC主题RFC-005Firefox NSS 加密与主密钥派生RFC-008文件获取与平台差异处理如果你想深入验证本文所述内容可以直接阅读仓库中的以下源码与测试存储布局定义browser/firefox/source.goProfile 发现与解析调度browser/firefox/firefox.go、browser/firefox/profile.go各类别解析器browser/firefox/extract_password.go、extract_cookie.go、extract_history.go、extract_download.go、extract_bookmark.go、extract_extension.go、extract_storage.go对应测试browser/firefox/ 目录下的*_test.go文件SQLite 打开与journal_modeoff实现utils/sqliteutil/query.go、utils/sqliteutil/sqlite.go结语Firefox 的数据存储与 Chromium 系有本质差异随机命名的 Profile、内容驱动的 Profile 探测、places.sqlite的多类别共享、明文 Cookie、仅密码加密以及微秒/毫秒/秒混用的时间戳体系。理解这些底层布局是正确实现 Firefox 数据提取的前提。RFC-004 给出了完整的地图而browser/firefox/源码则把地图上的每一条路径都变成了可运行的提取逻辑——对照本文的表格与代码片段你可以在自己的工具链中快速复现或扩展 Firefox 数据的解析能力。赞分享网络安全应用安全密码学CLI【免费下载链接】HackBrowserDataExtract and decrypt browser data, supporting multiple data types, runnable on various operating systems (macOS, Windows, Linux).项目地址https://gitcode.com/gh_mirrors/ha/HackBrowserData点击查看免费下载相关推荐PhxQueue消费者最佳实践如何实现高吞吐消费与负载均衡PhxQueue消费者最佳实践如何实现高吞吐消费与负载均衡 PhxQueue是一个基于Paxos算法的高可用、高吞吐、高可靠分布式队列本文将分享消费者端的最H5-Dooring 服务端数据存储目录结构全解析server/public 数据文件布局与私有化部署运维指南H5 Dooring 服务端数据存储目录结构全解析server/public 数据文件布局与私有化部署运维指南 本文基于 H5 Dooring 部署运维文档前端低代码AI_NovelGenerator 完整指南如何用 AI 小说生成 4 步写出一部长篇AI_NovelGenerator 完整指南如何用 AI 小说生成 4 步写出一部长篇 你想用 AI 写一部多章节长篇小说又怕写久了角色崩人设、剧情拖到后期人工智能大模型AI 应用AI 写作RAG桌面应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑