讲一个我和浏览器死磕的故事。
几个月前我实在受不了 Chrome 越来越重的肉身和没完没了的数据采集,又不想回到那个动不动就飙内存的老 Firefox,于是花了几个周末,在一台吃灰的 Linux 机器上动手搞了一个自己的浏览器。名字就叫camofox-browser——取 "camo"(伪装)和 "fox"(火狐)的组合,核心思路很简单:以 Firefox 为底子,通过源码编译和深度配置,做一个反指纹、抗追踪、用完即走的隐私向浏览器。这篇文章就是我整个项目的完整复盘,从设计思路到编译落地,再到日常使用踩过的坑,一次性都倒出来。
不管你是对浏览器工作原理好奇的普通用户,还是想自己折腾一个干净浏览环境的技术爱好者,这篇都值得你看看。我不讲什么高深理论,就讲我实际怎么想、怎么改、怎么编译、怎么用,每一步都是能直接照做的。
1. 项目核心思路与整体架构拆解
1.1 为什么底子选 Firefox 而不是 Chromium
这个决定是我最开始做的,也是影响后面所有工作的最根本选择。市面上绝大多数第三方浏览器,包括一些所谓的"隐私浏览器",都是拿 Chromium 换层皮。Chromium 本身不差,但它的生态里谷歌的服务和追踪逻辑实在太深入了,你很难在不动大手术的情况下把它彻底清理干净。而且 Chromium 的代码库这几年越来越庞大,编译一次动不动就要一两个小时,对个人开发者极不友好。
Firefox 这边情况完全不同。首先它的渲染引擎是 Gecko,不是 Blink,在内核层面就和 Chrome 生态划清了界限。其次 Mozilla 在 Firefox 里内置了不少隐私相关的底层接口,比如privacy.resistFingerprinting(反指纹)、privacy.clearOnShutdown(关闭时清理)、network.dns.echconfig.enabled(ECH 连接加密)之类的开关,这些东西在 Chromium 里要么没有,要么就是被砍得只剩个壳。最关键是 Firefox 的源码结构相对清晰,mach构建系统也很成熟,个人开发者想定制自己的版本,门槛比 Chromium 低不少。
我实际编译下来,Firefox 源码树加上构建依赖,大概需要 30GB 左右的磁盘空间,首次编译在 8 核 16G 内存的机器上大约耗时 40 分钟。Chromium 的话,同样的机器没两个小时下不来。对我这种急性子来说,Firefox 几乎就是唯一解。
1.2 camofox-browser 要解决的实际问题
我做这个项目不是为了装酷,是真心受够了三件事。
第一是"指纹追踪"。现在的广告系统早就不靠 cookie 认人了,而是通过 Canvas 指纹、WebGL 参数、字体列表、时区语言、屏幕分辨率这些杂七杂八的信息,拼出一个几乎唯一识别你的"数字指纹"。这个指纹你根本清不掉,换个浏览器、开无痕模式都没用。我实测过,一个没做任何防护的 Firefox,在指纹检测网站上能拿到接近 95% 的唯一性评分——这和裸奔没什么区别。
第二是"数据沉淀"。正常的浏览器会把浏览历史、缓存、LocalStorage、IndexedDB 这些东西统统留在硬盘上,越积越多。这带来两个问题:一是隐私风险,只要有人拿到你电脑,基本就等于拿到了你最近几个月的全部网上行踪;二是性能损耗,臃肿的缓存和数据库会让浏览器越用越慢。
第三是"环境一致性"。同一个网站,在不同的浏览器配置下看到的页面可能完全不一样——有的是因为你被识别成了不同的设备,有的是因为网站做了 A/B 测试。我想让浏览器最大程度地"隐藏自己的存在感",让每个网站都把我当成一个普通访客,而不是一个可以被精准定位的目标。
所以 camofox-browser 的定位就很清晰了:一个基于 Firefox 源码的、默认高隐私强度的、强调反指纹和自动会话清理的桌面浏览器。它不是什么大项目,也没有商业计划书,就是一个能把我自己从 Chrome 的数据泥潭里捞出来的工具。
1.3 整体功能架构一览
我先在脑子里把需要的功能画了一张表,然后每一条都在 Firefox 的源码和配置项里找到了对应的落地方案:
| 功能模块 | 预期效果 | Firefox 中的实现方式 |
|---|---|---|
| 指纹随机化 | 每次会话的 Canvas、WebGL 参数不同 | privacy.resistFingerprinting+ 自定义噪声注入 |
| UA 标识切换 | 网站识别为普通 Firefox 而非定制版 | general.useragent.override配置 |
| 会话自毁 | 关闭浏览器后清除全部临时数据 | privacy.clearOnShutdown系列配置 |
| 默认防追踪 | 第三方 Cookie、跟踪器全部拦截 | network.cookie.cookieBehavior+ 追踪保护列表 |
| 安全 DNS | 防止 DNS 查询被窥探 | network.trr.mode配置 DoH |
| 禁用遥测 | 不上传任何使用数据 | 源码级去除遥测代码 + 配置禁用 |
设计的时候我给自己定了一个原则:底层能力优先靠配置实现,配置搞不定的才动源码。原因很简单,直接改配置和预置文件,后续升级维护容易,而且不容易引入编译错误。真正需要动源码的部分,主要就是默认 UA、默认主页、默认搜索引擎这几处,工作量不大,但效果立竿见影。
2. 核心功能实测:反指纹、防追踪与数据自毁
2.1 反指纹到底在反什么
很多人一听"反指纹"就觉得是玄学,其实它背后的原理很朴素。浏览器在渲染网页的时候,会通过 Canvas 接口画一些图形、用 WebGL 做一些 3D 渲染、加载本地字体列表、读取系统时区和语言设置,这些操作的结果会因为你的设备型号、显卡驱动、操作系统版本而略有不同。把这些差异组合起来,就能形成一个足够区分你跟其他人的特征码,而且你根本看不到它。
camofox-browser 主要做了三层防护。第一层是统一化,通过privacy.resistFingerprinting把字体列表、时区、语言这些信息"模糊"成一个通用值,让所有用户的指纹看起来都差不多;第二层是噪声化,在 Canvas 和 WebGL 的渲染结果中注入一个极小的随机误差,这样每次拿到的指纹都不完全一样;第三层是禁用,对于能关就关的探测接口尽量关闭,比如navigator.deviceMemory这种专门暴露硬件参数的 API。
我实测过开启这三层防护前后的差异。同样一台机器,未开启时指纹检测网站的熵值(entropy)在 8.2 位左右,意味着大约 300 台设备里就能区分出我这一台;开启之后熵值降到了 4.1 位左右,也就是几千台设备里才会有两台长得跟我差不多。虽然没有做到完全隐身,但已经足够混进人群里了。
这里要特别提醒一点:resistFingerprinting开启后会有一个副作用,就是 Canvas 的读取性能会下降一点,因为每次渲染都要多一步噪声注入。日常浏览感觉不明显,但如果你经常玩网页版的图形编辑器或者比较吃 Canvas 的在线工具,可能会觉得稍微有点"肉"。这不是 bug,是隐私和性能的正常取舍。
2.2 自动数据自毁机制怎么调最安全
Firefox 原生就带了一套关闭时清理数据的开关,但默认是全部关掉的。我把它们全打开了,同时做了一些细调。核心配置如下:
// 关闭浏览器时清理全部临时数据 privacy.clearOnShutdown.cache = true privacy.clearOnShutdown.cookies = true privacy.clearOnShutdown.downloads = true privacy.clearOnShutdown.formdata = true privacy.clearOnShutdown.history = true privacy.clearOnShutdown.offlineApps = true privacy.clearOnShutdown.sessions = true // 同时关闭 Firefox 自带的 "个性化推荐" 数据上报 datareporting.healthreport.uploadEnabled = false datareporting.policy.dataSubmissionEnabled = false有几个细节值得单独说。书签和下载文件夹是不会被清理的,因为它们的存储位置不在clearOnShutdown管理的范围内,这也符合直觉——你存下来的东西应该保留,但网上产生的垃圾应该消失。另外,cookies这一项我建议选择"清理全部"而不是"只清理第三方 Cookie",因为既然追求用完即走,就没必要留着任何站的 Cookie 过夜。
还有一个容易忽略的地方是sessions清理。它管的是 Firefox 的会话恢复功能,如果不开这一项,即使你清了缓存和 Cookie,只要你重启浏览器,Firefox 还是会根据之前的会话文件把标签页恢复出来,这时候网站照样能通过 LocalStorage 里的残留数据识别你。所以sessions必须开。
这样配置完之后,我的浏览器基本就是"薛定谔的浏览器"——用的时候什么都在,一关就什么都没有了。好处是极其干净,坏处是每次打开都要重新登录网站、重新输入表单。为了平衡体验,我妥协了一个地方:给少数几个可信网站(比如自己的邮箱、网盘)设置了"站点级 Cookie 白名单",通过 Firefox 的权限管理器单独放行,这样它们能在关浏览器后保留登录态,其他网站一律不留痕迹。
2.3 搜索、DNS 与流量层面的隐私加强
指纹和 Cookie 解决的是"网站认识你"的问题,但还有一个更基础的问题:你的浏览器在网上说话的时候,别人能不能听到你说了什么?这就涉及 DNS 和协议层面。
Firefox 从 90 版本开始默认支持 DoH(DNS over HTTPS),但默认是关闭或"仅在某些情况下启用"。我在 camofox-browser 里把它改成了强制开启:
network.trr.mode = 3 // 3 = 强制 DoH network.trr.uri = "https://mozilla.cloudflare-dns.com/dns-query"mode=3的意思是所有 DNS 查询都走 HTTPS 加密通道,不走系统默认的 UDP 53 端口明文查询。这样一来,Wi-Fi 管理员或者运营商能看到的数据就少了很多——他们最多知道你访问了哪个 IP,但看不到你要解析的域名。如果你对 DoH 服务器有什么顾虑,也可以换成自己想用的服务商,在network.trr.uri里指定就行。
还有一个容易被忽略的设置是network.dns.disablePrefetch(禁用 DNS 预取)和network.prefetch-next(禁用网页预加载)。这两个功能本来是加速用的,但它们会在你还没点开链接的时候,就提前去解析和加载目标网站,等于变相暴露你的下一步行为。我都关掉了。实测对网速感知影响不大,毕竟现在网络带宽普遍过剩,少一点"贴心"的预测行为,多一点确定性,反而更踏实。
最后是协议层面,Firefox 默认开启 ECH(Encrypted Client Hello)支持——这个功能对普通用户来说基本上是无感知的,它能保证你访问某个网站时,连接握手过程中发送的域名信息也是加密的。我在 camofox-browser 里确认了network.dns.echconfig.enabled和network.dns.use_https_rr_as_altsvc都是默认开启状态,这样整条链路从 DNS 解析到 TLS 建立,都尽可能保持了内部的机密性。
3. 从源码到浏览器:完整编译实操流程
3.1 环境准备和依赖安装
如果你看到这里决定自己也动手试一把,那接下来的内容是重点。整个编译流程我按"能最小改动绝不大动"的原则推进,花在环境准备上的时间比想象中多。
我的硬件环境是 i5-8400 六核处理器、16GB 内存、500GB 空闲磁盘的 Ubuntu 22.04 机器。Firefox 源码编译的最低要求是 8GB 内存、20GB 磁盘,但我强烈建议内存给到 16GB,不然链接阶段很容易 OOM。
系统依赖直接一把梭:
sudo apt update sudo apt install build-essential python3 python3-pip \ mercurial libgtk-3-dev libgtk-3-0 libdbus-glib-1-dev \ libasound2-dev libpulse-dev libx11-dev libxcomposite-dev \ libxdamage-dev libxext-dev libxfixes-dev libxrandr-dev \ libxt-dev libgconf2-dev libdrm-dev libgbm-dev \ nasm yasm autoconf2.13 clang llvm这里有个关键点:Firefox 官方文档推荐用 Clang 而不是 GCC 来编译,原因是他们在 CI 里主要验证的是 Clang 工具链。如果你用 GCC,运气好也能编过,但一旦遇到奇怪的编译错误,你没法确定是自己配置的问题还是工具链兼容问题,排查成本极高。所以别折腾,直接安 Clang。
源码获取我用的是官方 Mercurial 仓库,因为 Firefox 的源码管理主要用 Mercurial,Git 镜像虽然存在但同步有延迟:
hg clone https://hg.mozilla.org/mozilla-central cd mozilla-central如果你想省时间,也可以只克隆深度为 1 的版本:
hg clone --stream https://hg.mozilla.org/mozilla-central --config phases.publish=False这个仓库大小大概在 5GB 左右,下载速度取决于网络环境,我用了大概 20 分钟。
3.2 修改源码:打造属于你自己的标识
源码拿到手里,第一件事不是急着编译,而是先在几个关键位置留下你的标记。我改了四处:
第一处是浏览器用户代理(User-Agent)。默认 UA 里会带有 Firefox 的版本号,如果不改,别人一眼就知道你用的是原版 Firefox 而不是自己的定制版。我找到了netwerk/protocol/http/nsHttpHandler.cpp,把默认的 UA 字符串重写为:
Mozilla/5.0 (X11; Linux x86_64; rv:115.0) Gecko/20100101 Firefox/115.0这样既保持了和现代网站的兼容性,又隐藏了定制痕迹。这里有个注意事项:UA 字符串的格式必须严格遵循标准,rv后面的版本号最好和实际编译的版本一致,否则部分网站会因为你 UA 和页面代码不匹配而出现错版。
第二处是默认主页。我改的是browser/app/profile/firefox.js里的browser.startup.homepage配置,直接设成一个空白页,不让它去访问 Mozilla 的什么内容页面。第三处是默认搜索引擎,把默认搜索从 Google 换成了一个不追踪用户的无痕搜索服务。第四处是把代码里所有的mozilla.org品牌标识替换成自己的camofox字样,这个纯属个人趣味,不改不影响功能。
这里要特别强调:改代码之前先备份,改完之后编译前再 review 一遍。我就吃过亏,有一次在nsHttpHandler.cpp里加了一行中文注释,结果忘了保存文件编码,编译到一半直接报错说非法字符,我花了快半小时才定位到问题。
3.3 创建 mozconfig 并执行编译
Firefox 的构建配置都集中在根目录的mozconfig文件里。我写的完整配置如下:
# 基础编译选项 ac_add_options --enable-application=browser ac_add_options --enable-optimize ac_add_options --disable-debug # 禁用遥测和数据上报 ac_add_options --disable-telemetry ac_add_options --disable-crashreporter # 启用自定义品牌 ac_add_options --with-app-name=camofox ac_add_options --with-app-basename=camofox-browser ac_add_options --with-branding=browser/branding/camofox # 减少非必要组件 ac_add_options --disable-webservices ac_add_options --disable-safe-browsing # 优化构建速度 mk_add_options MOZ_MAKE_FLAGS="-j8"逐行解释一下。--enable-optimize和--disable-debug是给正式版用的,编译出来的体积更小、运行更快。--disable-telemetry在源码层面直接移除了遥测代码,比事后配置更彻底,因为有些数据采集逻辑是你关不掉的。--disable-crashreporter同理,崩溃报告器本身也是个数据出口,不要也罢。--disable-webservices会关掉 Firefox 的一些云同步功能,如果你像我一样不需要账户体系,这个选项能省不少编译时间。--disable-safe-browsing有争议——它禁用 Google 的安全浏览服务,能减少对外通信,但也意味着恶意网址警告失效,纯粹是隐私和安全的权衡。
最后的-j8是并行编译参数,8 表示用 8 个线程同时编译。注意这个数字最好不超过你 CPU 的物理核心数,否则会频繁切线程反而变慢。
配置写好后,开始编译:
./mach build首次编译会下载一堆工具链和依赖组件,这阶段我一度以为卡死了,其实它会在后台慢慢跑。整个过程在我的机器上大概 40 分钟,期间风扇转得跟起飞一样,如果你机器散热一般,记得放它在一个通风良好的地方。编译完成后执行:
./mach run看到自己名字命名的浏览器窗口弹出来那一刻,说实话还挺有成就感的。如果你想打包成独立的安装包:
./mach package产物会放在dist/目录下,具体格式根据你的系统不同,有的是 tar.bz2 压缩包,有的是对应发行版的安装包。
4. 日常使用配置与隐私基线
4.1 装完必调的 about:config 重灾区
编译完成、装上系统之后,你以为就完事了?还早。源码配置只是地基,真正的精细调整都在about:config里。我整理了一份日常使用中觉得收益最大的配置清单,建议按需开:
| 配置项 | 推荐值 | 作用 |
|---|---|---|
privacy.resistFingerprinting | true | 全局反指纹,统一字体/时区/UA |
privacy.firstparty.isolate | true | 按顶级域名隔离所有存储 |
network.cookie.cookieBehavior | 1 | 只允许同站 Cookie |
network.http.referer.XOriginTrimmingPolicy | 2 | 跨域请求只发送裸域名 Referer |
browser.privatebrowsing.autostart | true | 启动即进入隐私窗口 |
media.peerconnection.enabled | false | 禁用 WebRTC,防 IP 泄漏 |
geo.enabled | false | 关闭地理位置接口 |
dom.storage.enabled | false | 完全禁用 DOM 存储 |
这里面privacy.firstparty.isolate我要多说一句。它开启后,不同网站的 LocalStorage、Cookie 会被彻底隔开,同一个第三方脚本在 A 网站存的数据,在 B 网站完全读不到,等于把跨站追踪的路全堵死了。代价是有些网站的多站登录会变得麻烦——比如你用同一个账号登录了博客和论坛,它俩之间互相跳转时可能不会共享登录态,需要你重新登录一次。这个权衡我觉得值。
media.peerconnection.enabled我是直接关了的。WebRTC 可以用来做语音视频通话,但它有一个著名的漏洞:即使你在后台挂了代理或者走了加密链,它也能通过 STUN 请求把你的真实内网 IP 甚至公网 IP 暴露出来。如果你平时不用浏览器开视频会议,干脆关掉最省心。
4.2 隐私和易用的平衡策略
把上面这些配置全部打开之后,你就拥有了一个"连自己都不太认得出"的浏览器。但这也会带来一个现实问题:很多网站会把你当成机器人,验证码能烦死你。我在实际使用中摸索出一个分级策略,分享给大家。
最高隐私档:用于浏览新闻、看文档、逛社区这类不需要登录的网站。开启所有反指纹和隔离配置,Cookie 全部按会话管理,关浏览器就清。
中间平衡档:用于需要登录的网站(比如邮箱、网盘、购物)。这时候我会临时把privacy.resistFingerprinting关掉,或者干脆直接用原版 Firefox 开一个单独的 Profile 来处理。因为很多网站的登录风控系统对"异常浏览器指纹 + 无 Cookie"的组合非常敏感,你配置越"隐身",它越觉得你可疑。
最低隐私档:用于网银、政务网站这类对安全性和兼容性要求极高的场景。这时候我直接切换到系统里装的原版 Safari 或 Chrome,因为它们被所有网站的风控系统充分测试过,兼容性最好。这不是说 camofox-browser 不能用,而是没必要在这种场景下给自己添堵。
最后再分享一个我一直在用的验证方法:用指纹检测网站来检验配置效果。这些网站会展示你能被识别出的熵值,以及模拟出的设备指纹。每次调整完配置,我都会刷新页面看看熵值变化。理想状态下,你应该能看到和你相同系统、相同语言、相同分辨率的用户有好几百个,这样你就成功"隐身"在人群里了。如果熵值依然很高,说明还有某个暴露源没堵上,回去继续排查。
5. 常见问题与排查技巧实录
5.1 编译阶段的玄学问题
编译 Firefox 的过程不会总是一帆风顺,我遇到的坑能凑满一张大桌子。
第一个坑是磁盘空间不足。Firefox 构建过程中产生的中间文件和符号文件非常惊人,我一开始以为 30GB 绰绰有余,结果编译到一半系统提示磁盘满了。后来我用ncdu排查发现,光obj-x86_64-pc-linux-gnu这个构建目录就吃掉了 18GB。解决办法是在编译前用du -sh *预估一下空间,并在mozconfig里加一行mk_add_options MOZ_OBJDIR=$HOME/camofox-build,把构建目录放到空间更大的分区。
第二个坑是链接阶段内存不足。第一次编译到 90% 左右时,链接器因为占用内存过大直接被杀掉了。这是 Firefox 编译的经典问题,解决方案是在mozconfig里加:
ac_add_options --disable-linker=lld export LDFLAGS="-Wl,--no-keep-memory"或者干脆加上-j4降低并行度,虽然慢一点,但稳定得多。我最后是用-j6+--disable-linker=lld过的关。
第三个坑是Clang 版本太新导致旧代码 warning 变 error。这个有点无解,因为 Mozilla 官方仓库总是在追踪最新工具链,但你本地装的 Clang 版本可能比预期的老或新。我的建议是装两个版本的 Clang 备用,万一新版编不过就切到旧版试试,反之亦然。
5.2 日常使用中的兼容性问题
编译成功只是万里长征的第一步,真正让我血压升高的是各种网站的兼容性问题。
首当其冲的是登录态保持。开了clearOnShutdown全清模式之后,你每次重新打开浏览器都要重新登录,这倒还好。真正的坑是有些网站会在你登录后通过 IndexedDB 存储一个"设备信任"标记,如果这个标记被清了,它就会强制让你做二次验证。我的解决方案是给少数常用网站设置 Cookie 白名单,在站点权限里允许它们"保存数据",这样这些网站的 IndexedDB 就不会被清掉,其他网站照旧。
第二个问题是部分视频网站播放异常。原因大概率是启用了resistFingerprinting后浏览器的 Canvas 输出和网站的播放器做了兼容性校验,算出来的哈希值不稳定。这种情况我没找到特别优雅的解决办法,目前就是手动在about:config里临时把privacy.resistFingerprinting关掉,看完视频再打开。反正配置项切换只需要在地址栏改一下值,重启就生效,不算太麻烦。
第三个问题是某些网站加载不出图形。这通常和dom.storage.enabled=false有关,因为不少现代前端框架(比如 React、Vue 开发的后台管理系统)会把一些初始化数据放在 LocalStorage 里,禁用后逻辑会崩。如果你日常需要用到这种业务型网站,建议把dom.storage.enabled改回默认,只保留其他隔离措施,效果差不了太多。
5.3 一个排查指纹暴露的实战案例
最后分享一个我排查了很久的案例,它让我对浏览器指纹的理解又深了一层。
有一天我配置完所有隐私选项后,在指纹检测网站上一测,其他指标都很好,唯独 WebRTC 的 DNS 解析泄露暴露了我的内网 IP。我明明已经关了media.peerconnection.enabled,怎么还会泄漏?后来发现原因是 Firefox 的 WebRTC 这个开关只管新建连接,但系统层面的 mDNS 解析服务仍然在跑。最后我是通过修改about:config里的media.peerconnection.ice.default_address_only为true,并同时把media.peerconnection.ice.obfuscate_host_addresses保持启用才解决的。这个经历告诉我,一个隐私功能是否真的生效,不能只看配置项名字,一定要用工具实测。
这也是我做 camofox-browser 最大的收获:隐私不是某一个开关能搞定的,而是一个需要反复验证、不断调整的系统工程。每一个配置项背后都有它自己的逻辑和副作用,你必须理解它们,才能真正掌握自己的浏览器。
写在最后:一点真心话
Camofox-browser 到现在已经是我每天的主力生产工具了。它不完美——有些网站用起来会别扭,偶尔也会遇到奇怪的兼容问题——但它让我重新找回了"上网是自己的事"这种感觉。每次点开那个还带着点编译气味的深蓝色图标,我都知道这个浏览器背后没有隐藏的数据暗渠,没有莫名其妙的推荐,也没有无声的监听。
如果你也想折腾一个自己的浏览器,我给的建议是:别急着抄我全部配置,先原版 Firefox 用一周,然后一两个星期加一个隐私设置,用指纹检测网站验证效果,慢慢磨合出最适合自己的组合。这个过程本身,就是对自己数字生活的一次重新掌握。