从源码定制camofox浏览器:Firefox抗指纹构建实战
发布时间:2026/9/10 7:47:14 锦皓数字建站

camofox-browser 这个项目是我最近一个月里做过最折腾、也最值的一个周末计划。起因很普通我在做前端埋点联调时顺手抓了一下自己浏览器的请求头结果发现里面不仅带着完整的 User-Agent连系统里装了哪些字体、屏幕分辨率、显卡 WebGL 渲染器的型号都暴露得一清二楚。那一刻我忽然意识到浏览器就是一台信息自助餐车任何第三方脚本都能随手舀走一大勺。于是我决定从 Firefox 的源码开始定制编译一个把数字指纹大幅抹平的浏览器项目代号就叫 camofox——camo 加 fox伪装版的火狐。这篇文章会把它的定位、构建流程、指纹干扰原理和实战效果完整记录一遍适合想自己定制浏览器的开源玩家、前端开发者以及所有对隐私追踪不太放心的人参考。1. 从血肉之躯的指纹聊起为什么我非要自己做浏览器1.1 一个浏览器在后台泄露了多少信息很多人都以为只要不登录账号、不填手机号网站就不知道你是谁。这个想法在十年前勉强成立放到现在已经完全不现实了。打开一个普通营销页面在脚本眼里你至少暴露了这些信息User-Agent 和 HTTP 头里的 Accept-Language、Accept-Encoding屏幕分辨率、色彩深度、设备像素比时区、系统语言、触控点数量Canvas 渲染同一段文字产生的哈希值WebGL 返回的显卡型号、渲染器厂商已安装字体列表硬件并发数、电池状态、传感器数据WebRTC 连接时通过 STUN 协商暴露的本地 IP。单独看哪一条都不至于精确定位到你但组合起来这个集合在人群里的唯一性极高。学术研究和商业追踪领域都验证过一件事大部分桌面浏览器的指纹在数十万样本中基本能保持唯一。更麻烦的是这种指纹和 Cookie 无关你就清空缓存、开隐身模式也没用画布还是能画出差不多的结果显卡还是报告同一个型号。我当时做了个实验在同一个系统里装两个不同内核的浏览器开无痕模式访问同一个指纹测试页两个浏览器返回的 Canvas 哈希和字体列表高度相似。因为底层硬件、系统字体、GPU 驱动是一样的浏览器外壳换了也藏不住。要真正藏住必须在浏览器层面主动做干扰。1.2 为什么最终选了 Firefox 而不是 Chromium市面上能改源码的浏览器主要是两大阵营Chromium 和 Firefox。我一开始确实考虑过 Chromium毕竟它的渲染引擎在绝大多数站点兼容性最好而且构建生态很成熟。但深入看下来Chromium 的隐私侧改动受 Google 产品策略影响很大很多反追踪能力需要额外装扩展而在 Manifest V3 时代扩展的权限又在被收紧。Firefox 这边的处境反而更适合折腾about:config提供了大量底层开关能对网络、媒体、渲染做细粒度控制内置了privacy.resistFingerprintingRFP机制这个能力在 Firefox 中已经发展多年代码成熟度不是靠脚本注入几个随机值能比的扩展 API 更开放uBlock Origin、NoScript 这类隐私利器在 Firefox 上的能力比在 Chromium 上完整构建系统mach对个人定制很友好不需要搭复杂的 depot_tools 那套流水线。另外一个隐藏因素Firefox 和 Tor Browser 是同源的。Tor Browser 基于 Firefox ESR 做深度隐私改造这意味着很多抗指纹补丁是直接存在于 Firefox 源码里的只是默认没打开。我把 camofox 定位成便携版反指纹浏览器时吃到的其实是 Mozilla 这么多年做隐私工程的存量资产。1.3 边界想清楚它不是 Tor也不是隐形人外套做这个项目之前我给自己划了一条心理边界camofox 的目标不是让你像 Tor Browser 那样在极端威胁模型下做到近似匿名而是把数字身份的暴露面压到最小。Tor Browser 的保护等级很高但代价是速度、可用性和大量站点兼容问题它适合特定场景不适合当日常主力。camofox 的日常使用预期是同一个网站在后台拿到的指纹信息非常有限被追踪系统从人群里单独挑出来的概率大幅下降。但这不代表你可以在里面为所欲为登录账号后服务端依然可以通过账号体系识别你输入过手机号的地方依然能关联到你的现实身份。做这个项目最主要的价值是让那些你根本没主动提供身份信息的场景里第三方无法轻易拼出你的画像。2. 工程落地camofox 从拿源码到出安装包的完整流程2.1 构建环境那些坑磁盘、内存、编译工具链Firefox 的全量构建是个资源大户。我第一次在 8GB 内存的笔记本上尝试结果在链接libxul.so那一步系统直接卡死风扇狂转十几分钟最后不得不用SIGKILL强制结束。所以先说清楚硬件底线16GB 内存起步磁盘剩余空间最好保留 150GB 以上。源码本体大概 5GB但构建产物、objdir、CCache 缓存加起来非常夸张我实际用了差不多 100GB 才跑完一次完整构建。操作系统我用的 Ubuntu 22.04 LTS构建前先装好基础工具链再执行./mach bootstrap。这个命令会自动把编译需要的 LLVM/Clang、Rust、NASM、GTK 开发头文件、Python 依赖全部装好。有一点要特别提醒不要自作聪明用系统自带的最新 LLVMmach 对版本有严格校验版本不对会在链接期报出各种莫名其妙的问题。所以我后来学乖了bootstrap 装什么就用什么。源码获取建议直接克隆mozilla-unified仓库因为后续要跟随 ESR 节奏更新unified 仓库切换分支比较方便。命令大概是hg clone https://hg.mozilla.org/mozilla-unified cd mozilla-unified ./mach bootstrap如果网络条件一般这一步会非常磨人建议预留一整个下午。bootstrap 完成后再跑构建我的 8 核 16GB 机器上首次构建约 2 小时出头后续增量构建基本在 20 到 40 分钟。2.2 mozconfig 的每一行都代表一个决定Firefox 构建的定制入口是.mozconfig文件。这个文件里每一行ac_add_options都是在决定你要一个什么样的浏览器。camofox 的配置我维护成了camofox.mozconfig内容如下ac_add_options --enable-applicationbrowser ac_add_options --enable-release ac_add_options --disable-debug ac_add_options --enable-optimize-O2 ac_add_options --disable-tests ac_add_options --disable-crashreporter ac_add_options --disable-updater ac_add_options --enable-hardening ac_add_options --with-app-namecamofox ac_add_options --with-app-basenamecamofox ac_add_options --with-brandingbrowser/branding/camofox mk_add_options MOZ_OBJDIRTOPSRCDIR/obj-camofox mk_add_options CCACHE1 export CCACHE_DIR$HOME/.cache/camofox-ccache逐条解释几个关键选择。--disable-tests能砍掉大量测试文件构建产物体积和编译时间都能降下来--disable-crashreporter和--disable-updater是隐私定制浏览器很关键的两项前者避免自编译版出现崩溃上报逻辑后者防止浏览器试图连接到 Mozilla 的更新服务--enable-hardening开启额外的安全加固选项包括栈保护、ASLR 相关功能。--with-branding这里指向一个自定义品牌目录目录不存在构建直接失败后面单独讲。之前也尝试过把遥测相关的宏在编译期完全关闭但实际测试发现很容易导致某些组件行为异常后来改成了通过distribution/policies.json和user.js在运行时禁用遥测效果一样还更灵活。2.3 从 mach build 到 mach package构建命令与产物处理配置写好后构建流程就是标准的 mach 三步build、package、run。cd mozilla-unified cp camofox.mozconfig .mozconfig ./mach build ./mach packagemach build完成之后二进制产物在obj-camofox/dist/bin/firefox这个文件可以直接跑。mach package会把整个浏览器目录打包成target.tar.xz方便分发给别的机器。这里有个细节打包完成的目录里除了主程序还有一堆组件、.so文件、扩展目录别只拷贝一个firefox可执行文件否则跑起来会缺这缺那。首次构建前我强烈建议在.mozconfig里开启 CCache也就是上面写到的mk_add_options CCACHE1和export CCACHE_DIR...。原因很简单Firefox 的 C 代码量极大CCache 能在后续切换配置、升级源码时省下大量重新编译的时间。我实测开启后第二次全量构建时间直接砍半。中文语言包这块也踩过坑。自编译版的版本号通常和官方发布版有细微差异直接下载官方 langpack xpi 可能因为版本不匹配装不上。稳妥做法是在构建机里执行./mach build langpack-zh-CN生成与当前构建完全匹配的语言包然后在distribution目录里放好policies.json统一管理扩展和语言包。2.4 自定义 branding 的隐藏技巧Firefox 有商标保护不能拿官方品牌图标和名字直接塞进自编译产物里。我一开始用默认的unofficialbranding构建出来的浏览器应用名叫 Firefox图标也是默认的小蓝狐怎么看都不对劲。后面认真学习了一下 branding 机制发现就是在源码树下建一个独立的品牌目录。在browser/branding/camofox目录里需要放这些文件brand.properties定义brandShortName、brandFullName之类的资源字符串、default*.png多尺寸应用图标、firefox.ico或对应的窗口图标文件。示例的brand.properties内容很简单brandShortNamecamofox brandFullNamecamofox-browser brandShorterNamecamofox文件名和格式必须严格对齐构建系统会按路径找资源比如默认背景、安装图标、程序图标。这个目录建好之后--with-brandingbrowser/branding/camofox才能正常生效。自己画的图标不用多精致但至少得在任务栏里一眼能认出是不是 camofox避免和原版 Firefox 混淆。2.5 发布包里的 policies.json 一定要配好构建完成之后我会在发布目录里加一个distribution/policies.json。这是企业级策略文件Firefox 启动时会自动读取实现对遥测、更新、默认浏览器的统一约束。camofox 用的内容大致是{ policies: { DisableAppUpdate: true, DisableTelemetry: true, DontCheckDefaultBrowser: true, DontAskToInstallExtensions: true } }DisableAppUpdate很重要。自编译版没有官方更新签名如果更新策略还开着Firefox 会不停去探测更新并报错用策略直接锁死更新入口比挨个改about:config里的app.update.*省事得多而且用户层面改不回去适合分发给其他人。3. 抹指纹的关键动作被 camofox 改掉的每一个隐私开关3.1 RFP抵抗指纹的一键总开关在许多隐私浏览器里privacy.resistFingerprinting是当之无愧的基石。它不是一个简单的布尔开关而是一整套深度干预机制Canvas 读取被随机化、时区被强制为 UTC、屏幕尺寸被替换成标准值、字体枚举被裁剪、指针和事件的一些细节也被统一处理。形象点说普通浏览器是素颜出门RFP 是给所有暴露信息画了一张标准化脸谱。我在 camofox 的默认配置里开启的项配置项推荐值作用privacy.resistFingerprintingtrue开启核心抗指纹机制privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPromptstrue自动拒绝无用户输入触发的画布读取授权弹窗privacy.trackingprotection.fingerprinting.enabledtrue启用 Firefox 内置的指纹追踪保护列表privacy.trackingprotection.cryptomining.enabledtrue拦截恶意加密货币挖矿脚本webgl.disabledfalse严格模式下才设为truemedia.peerconnection.enabledfalse关闭 WebRTC防止本地 IP 从 SDP 中泄露media.navigator.enabledfalse禁止脚本枚举麦克风、摄像头设备geo.enabledfalse禁用地理位置接口network.http.sendRefererHeader1只在同源页面跳转时发送 Referernetwork.dns.disablePrefetchtrue禁止 DNS 预取减少链路痕迹browser.send_pingsfalse禁止超链接 ping 追踪device.sensors.enabledfalse禁用环境光传感器等硬件传感器接口dom.battery.enabledfalse禁用电池状态 APIbeacon.enabledfalse禁用 sendBeacon 后台上报这些配置我放在 profile 根目录的user.js文件里。user.js的机制很粗暴但有效每次浏览器启动时它会把里面的配置覆盖到prefs.js。也就是说哪怕某个网页脚本在页面里尝试修改这些设置下次重启又会恢复成 camofox 的默认值。这个特性对普通用户非常省心。3.2 Canvas、WebGL 与字体指纹的分头处理Canvas 指纹的原理是浏览器渲染一段文字或图形经过抗锯齿、字体渲染、显卡驱动差异之后每个设备产出的像素哈希都不一样。RFP 会在这个环节注入随机噪声让每次读取得到的结果不完全一致。我在测试中看到的表现是同一个页面刷新两次Canvas 哈希值经常不同连续多次后后台基本无法确定哪个是真实值。WebGL 的处理要谨慎一些。webgl.disabledtrue是彻底关闭指纹是藏住了但不少可视化页面、部分地图组件会直接失效。camofox 的折中方案是webgl.disabledfalse但把webgl.enable-debug-renderer-info设为false这样做能在保留 WebGL 功能的同时把 GPU 厂商和渲染器字符串对脚本隐藏掉。如果你对诡异兼容性问题不敏感直接关掉 WebGL 更干净。字体指纹是容易被忽略的一条。平时安装的一堆字体会让页面脚本通过document.fonts枚举出不完整但足够独特的清单。RFP 启用后网站只能看到一组标准字体列表比如普通英文、中文和少量通用字体真实装了几百个字体也暴露不出来。代价是某些花哨字体页面会退化为系统默认字体渲染视觉上没那么精致但可接受。3.3 WebRTC 这个最容易翻车的泄漏点很多人以为只要不打开音视频网页WebRTC 就不会工作。实际上浏览器加载包含 WebRTC 的脚本时可能会触发 STUN 协商通过RTCPeerConnection创建本地 SDP 候选把本机内网 IP甚至某些情况下的出口 IP 暴露给站点。这个泄漏和 JavaScript 权限无关普通网页就能读到。我在 camofox 里直接把media.peerconnection.enabled设为false。这个配置会让一部分在线会议、直播连麦、P2P 文件传输功能不可用但对日常浏览几乎没影响。你在浏览器里看视频、刷文章、购物根本不需要 WebRTC。真正需要开会的时候我会开另一个独立的普通配置 profile 来用避免把 camofox 的隐私设置和办公需求混在一起。另外还顺手关掉了media.navigator.enabled这个开关控制navigator.mediaDevices.enumerateDevices()的返回结果。哪怕是简单的设备枚举也会告诉网站你装了几个摄像头、几个麦克风这些信息在真实的追踪链里很有辨识度。关闭之后部分需要调用摄像头的网页在授权前会拿不到设备列表反过来提醒你这个站点对设备信息有额外的探查意图。3.4 软指纹细节时区、语言、Referer、存储与预取硬性的 Canvas/WebGL 解决后真正的差异化反而藏在软指纹里。时区就是一个典型如果你长期在东八区访问的站点却显示 UTC 时间后台会根据时区偏移量把你从一堆访问者里筛出来。RFP 启用的一个直接效果就是让页面脚本拿到的时区被统一处理不再反映真实地理区域。语言也同样。我把intl.accept_languages设置成en-US, en, zh-CN, zh浏览器发出的Accept-Language头就是这串标准值。我不追求伪装成纯英文用户反而要的是很多非英语母语者都会用这种列表的普通感。太独特的语言组合也是指纹。Referer 字段对隐私的影响经常被低估。默认情况下一个页面里的外部图片、统计脚本、字体服务都能拿到完整的来源 URL。camofox 把network.http.sendRefererHeader降到1只在同源导航时发送简化 Referer跨站第三方请求基本拿不到来源页地址。类似的小开关还有network.dns.disablePrefetch关掉之后网址还没点进去就不提前解析 DNS 了虽然省不了什么时间但确实少了一些数据痕迹。4. 上手实测指纹测试、正常上网与那些副作用4.1 指纹测试网站的前后对比空口无凭拿到 camofox 第一件事就是跑一轮指纹测试。我主要用了三个公开检测工具EFF 的 Cover Your Tracks、AmIUnique 和 BrowserLeaks。对比对象是同一个系统里默认配置的 Firefox。结果整理成表测试项默认 Firefoxcamofox严格模式User-Agent明确显示 Firefox 与操作系统版本统一的固定版本字符串Canvas 哈希稳定唯一多次刷新结果不一致WebRTC IP 检测可读取内网 IP检测结果显示不可用/无泄漏字体枚举完整字体列表仅少量标准字体屏幕分辨率真实分辨率与 DPR标准尺寸不随物理屏幕变化追踪保护评级部分指纹可被识别指纹识别难度明显提高直观感受是默认 Firefox 的测试结论往往接近你的浏览器在样本中具有较高唯一性而 camofox 的结论变成你的浏览器和大量用户相似。只要 canvas 哈希不稳定、字体列表被裁剪、WebRTC 关闭追踪系统能组合的维度就少了一大截。4.2 哪些网站会反抗隐私设置加强后不是所有站点都会安静接受。实测下来最典型的反噬是三类第一登录风控。部分银行、政务平台、社交网站的登录风控会结合浏览器指纹做安全判断当它发现 Canvas 哈希每次加载都在变、时区强制 UTC会认为这是可疑账号通常会弹出滑块验证码或短信二次验证。验证码倒没什么但如果你赶时间就会特别烦躁。第二视频网站的分辨率判断。RFP 下 H5 播放器拿到的屏幕尺寸不是真实屏幕参数于是某些视频站会认为你的屏幕是小尺寸设备最高只给你 1080p 甚至更低档位。想要 4K 原画的话camofox 这种严格配置反而成了阻碍。第三本地化和支付。UTC 时区对订票、秒杀、信用卡风控都有影响。我遇到过一次订国际机票时页面显示的日期比我所在时区早了半天差一点订错日期。所以我的原则是支付、购票、报税这类强身份场景不用 camofox切回普通浏览器查资料、逛论坛、刷内容、注册一次性账号时才用 camofox。4.3 日常使用的真实感受除了上述特殊场景camofox 的日常体验比我想象中好。启动速度因为编译时砍了测试和崩溃收集组件感觉比原版还快一点点内存占用和原版 Firefox 持平毕竟 Gecko 引擎的底子在不会因为隐私设置变重。默认安装的扩展我固定了三件套uBlock Origin、NoScript 和 LocalCDN。uBlock Origin 拦截广告和已知追踪域名NoScript 对不信任站点先禁掉脚本能杜绝一大类基于 JS 的枚举攻击LocalCDN 能把常见前端库从本地注入减少向公共 CDN 发请求。这三件套配合 RFP基本覆盖了字体、Canvas、请求链、第三方资源几个最主要的泄露路径。说实话我并不会把所有上网行为都搬进 camofox。有些内网管理系统、开发者工具调试、浏览器自动化测试用原版 Firefox 反而方便。camofox 更像是我的干净浏览器专门留给那些不想被建画像的浏览活动。5. 构建与日常使用中的真实踩坑记录5.1 编译中途 OOM把链接器换成 lld 并限制并发8GB 内存机器上构建 Firefox 最大的坎是链接阶段。libxul.so是一门重型链接任务GNUld占用内存能飙到十几 GB。我当时的解法分两步首先在.mozconfig里启用ac_add_options --enable-linkerlldLLVM 的 lld 链接器内存占用比传统ld低很多其次用mk_add_options MOZ_MAKE_FLAGS-j4把并行编译任务从默认的核数降下来虽然慢一点但系统不会卡死。如果机器还顶不住加 swap 是最后的兜底手段。这个方法只能救急构建机器还是建议 16GB 内存起步。5.2 LLVM 与 Rust 工具的版本地狱第一周踩得最多的坑跟工具链版本有关。Firefox 构建对 Clang 和 Rust 的版本要求很严格版本高了低了都可能链接失败。症状五花八门什么undefined reference、cannot find -lstdc、failed to build librustc其实是版本不匹配在作怪。最后我清理掉手动安装的 Rust重新跑./mach bootstrap让它统一安装官方 toolchain。之后出问题就先检查rustc --version和clang --version是否与 bootstrap 安装的一致排查效率高了很多。还有一点PATH里如果存在多个 Python 版本mach 也会抽风建议在.mozconfig顶部显式指定export PATH/usr/lib/rust-官方工具链路径:$PATH export CCclang export CXXclang具体路径以 mach 输出为准思路是让构建环境与环境变量保持一致避免系统里另一套交叉编译工具出来捣乱。5.3 关闭遥测后崩溃上报导致的怪问题刚构建完 camofox 时我发现每次打开 about:preferences 的隐私页面浏览器偶尔会弹一个崩溃报告提交对话框。问题是我已经在 mozconfig 里写了--disable-crashreporter理论上不该有这个组件。后来定位到运行时策略没有完全锁死Firefox 还是会往一些遥测端点发状态信息。解决办法是把toolkit.telemetry.enabled、datareporting.healthreport.uploadEnabled全部在user.js里设为false同时在distribution/policies.json里加上DisableTelemetry: true。重启后问题消失。这提醒我编译期关闭和运行期禁用是两回事自编译浏览器交付前一定要内外都检查一遍不能想当然。5.4 扩展签名与 AMO 的限制自己编译的 Firefox 默认和官方 Release 版一样只允许安装通过 AMO 签名的扩展。开发自定义扩展时特别麻烦每次都得折腾临时载入。我在开发机上直接把xpinstall.signatures.required设为false这样本地加载未签名扩展不受阻。但发布 camofox 给别人的时候这两者就矛盾了。关闭签名校验意味着用户可能装到被篡改的恶意扩展对普通用户风险偏高。我目前的方案是默认构建保持签名校验开启日常推荐用户去 AMO 安装官方签名的隐私扩展只有开发者版本额外提供一个签名校验关闭的构建谁也不影响谁。5.5 维护两个 profile严格模式和日常模式camofox 最后被我拆成了两套 profile。严格模式的 profile 开启所有 RFP 相关开关、关闭 WebRTC、关闭 WebGL用于指纹测试和敏感浏览日常模式的 profile 保留 WebGL 和一部分兼容性设置用于看视频、用在线工具。启动命令很简单# 严格模式 ./obj-camofox/dist/bin/firefox -P camofox-strict -no-remote # 日常模式 ./obj-camofox/dist/bin/firefox -P camofox-daily -no-remote两套 profile 的user.js分开维护互不干扰。这样既保住了日常可用性又不用在关键场景里降低隐私标准。如果你也打算复制这套方案建议一定把 profile 目录放在独立位置不要把默认 profile 和同一个浏览器混用否则配置覆盖起来会非常混乱。6. 关于 camofox 的后续想法和个人心得浏览器这个领域看起来已经被巨头垄断了但真正动手从源码搓一遍之后你会发现留给个人定制的空间比想象中大得多。Firefox 的构建系统虽然繁琐但每一步改动都有明确的反馈改一条 mozconfig构建出来的是不一样的浏览器改一组隐私开关指纹测试结果是可量化的。这种掌控感比单纯装个隐私插件要强太多。camofox 目前还在持续迭代。我下一步打算做几件事把构建过程写成 GitHub Actions 的 CI 脚本实现每天自动拉取代码、构建、出包的流水线跟随 Firefox ESR 节奏发版不追新功能保证稳定性和长期维护再做一个简单的user.js管理面板让非技术用户也能在这套配置的基础上微调不用每次手动改 about:config。最后分享一条个人经验任何隐私浏览器都不是银弹。camofox 能降低数字指纹的暴露度但真正有用的隐私习惯还包括少点接受全部 Cookie、少装来路不明的扩展、密码尽量用本地密码管理器而不是浏览器同步到云上。从源码构建的意义本质上不是造一个完全隐身的工具而是让你重新掌握哪些信息愿意给出去的选择权。祝你在折腾的路上也能收获我第一次跑通 camofox 时那种我的浏览器终于听我话了的快乐。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。