从零构建camofox-browser:Firefox隐私加固与反指纹配置实战
如果你在一个陌生网站停留 10 秒它会怎么记住你很多人以为靠的是 Cookie但真正麻烦的其实是浏览器指纹——从 UA、Canvas、WebGL、字体、时区到屏幕分辨率几十个特征被悄悄组合成一段几乎稳定不变的标识。我试过好几套隐私方案最后决定不折腾现成的轮子直接基于 Firefox ESR 做了一层可维护的隐私配置层项目代号就叫 camofox-browser。camofox 这名字很直白camo 是迷彩伪装fox 是 Firefox 那只狐狸目标就是让浏览器在保留日常可用性的前提下把指纹尽量抹平、打乱、统一。这篇记录会从立项动机一路写到踩坑修复适合两类人一是正想给 Firefox 做隐私加固的朋友二是自己维护开源项目、想知道指纹方案如何落地的人。你也可以把它当成一份带故事的 user.js 笔记照着抄配置能抄走一半剩下的那些“为什么要这么改”才是最有价值的部分。1. 为什么动手做 camofox-browser当网站比你还了解你的设备1.1 一次戳破安全感的指纹测试事情起因是我在一个无痕窗口里打开了一个指纹检测页面。注意是无痕窗口没有登录也没有带任何 Cookie。按理说浏览器应该像个陌生人。页面上确实什么都没显示但在后台脚本里它返回了一串hash我看着这串 hash 抱着最后一丝侥幸重启浏览器、再开无痕、再次访问。几秒钟后同一串 hash 又出现了。那一刻我才意识到无痕窗口能清掉的是“本地记忆”而不是“你留给网站的特征”。从操作系统、CPU 并发数、设备内存、屏幕大小到 canvas 绘制的图片、WebGL 渲染器名称、系统字体列表、音频处理后的波形这些信息不需要你点任何按钮JavaScript 就能悄悄读取。它们单拎出来都不算敏感组合起来却几乎可以做到“唯一识别”。对比一下这些特征能带来的信息量特征典型熵值为什么难替换User-Agent 平台中默认情况下真实且稳定Canvas/WebGL 指纹高和显卡驱动、渲染引擎高度相关系统字体列表高不同系统预装字体差异巨大屏幕分辨率、色深中容易被忽略但很稳定时区、语言、键盘布局中组合起来能缩小人群范围AudioContext 波形中高依赖底层音频硬件与驱动硬件并发数/设备内存低中navigator.hardwareConcurrency 直接暴露这几项组合起来在所有访问者中通常能产生极高的区分度。于是我做了个简单的估算如果我只有其中四五项可能无法从几千万人里揪出一个人但如果有完整组合enough不需要够到“唯一”只需要够到“你访问 A 网站和 B 网站的时间点能对上”就足够让广告网络和数据分析平台跨站跟踪你。1.2 现有方案为什么都不够顺手发现问题之后我第一时间想的当然是找现成方案。可当我认真对比时发现普通浏览器默认状态下的隐私能力其实很有限而“极致隐私”的浏览器又牺牲了太多日常可用性。普通 Firefox默认状态只是不主动帮网站建立长期追踪文件但 JavaScript 仍然可以自由读取 canvas、WebGL、字体信息。它自带的严格模式加强了对已知追踪器的拦截可对于“从零开始、动态生成特征”的指纹脚本拦截能力依然有限。Chrome/Edge 的隐私模式这类“无痕模式”解决的是设备上的残留问题不是网站侧的识别问题。打开无痕开着 DevTools 看一眼照样能拿到一堆硬件特征。Tor Browser在指纹统一这件事上是教科书级方案。很多浏览器做过反指纹扩展但最彻底的做法就是像它那样让所有用户的窗口尺寸、UA、语言、字体几乎一致。代价也很明显验证码频率高、视频播放经常卡、很多登录流程会莫名中断。作为日常浏览器绝大多数人撑不过一周。LibreWolf / Arkenfox 这类项目它们已经非常贴近我想要的结果。但对我个人来说还是有两个不舒服的点一是它们默认禁用的能力偏多装扩展、跑 WebGL 应用时经常要我去手动开开关二是配置更新节奏和上游 ESR 的绑定关系不够透明出了问题我很难判断是自己的配置冲突还是上游策略变了。所以我很明确不是要再做一个“更严格”的改版浏览器而是要做一个“在普通 Firefox 与极端隐私之间可调节、可解释、可回退”的隐私配置层。这就是 camofox-browser 的起点。2. camofox-browser 的架构一层配置、四类机制、一条流水线2.1 它到底改了什么从扩展机制到内核偏好设置camofox-browser 的定位并不是重新编译一个浏览器内核而是通过一套外部配置把 Firefox 从“能用”调教成“少说话”。这样做最大的好处是可审计、可回退、更新可以紧紧跟随 ESR 版本。整个项目在物理上由四部分组成camofox-browser/ ├── profile/ │ ├── user.js │ ├── containers.json │ └── extensions/ ├── policies/ │ └── policies.json ├── scripts/ │ ├── install.sh │ ├── build_firefox.sh │ └── verify_fingerprint.sh └── README.mdprofile/user.js是灵魂它会在 Firefox 启动时覆盖默认偏好设置。policies/policies.json是企业级策略文件用来锁住一些不想被用户在about:config里误改的项。profile/containers.json定义了多账号容器把不同站点的 Cookie 隔离进独立空间。scripts/里的东西则负责把以上文件组装成一个可以用独立 Profile 启动的实例。这样的分层结构让我在排错时非常舒服一个网站行为异常我可以先怀疑内容拦截规则再怀疑扩展噪声注入最后才怀疑 user.js 参数。每一层都能单独关掉问题定位通常是分钟级。2.2 四类机制的分工用一句话概括 camofox-browser 的工作方式让不该被读取的改成读不到让能读到的改成读不准让能读准的改成不一致同时把跨站追踪的路径全部断开。拆开看是四类机制在配合。偏好层通过 user.js 里的privacy.resistFingerprint、privacy.resistFingerprint.letterboxing、privacy.trackingprotection.fingerprinting.enabled这类原生参数改变浏览器的默认行为。这一层解决的是“系统要不要给你这么多信息”的问题相当于给浏览器上了一道总闸。内容拦截层基于 uBlock Origin 的静态规则和 Firefox 原生追踪保护在网站脚本运行前就把它拦下来。这针对的是“浏览器已经知道不该给那么多信息但第三方脚本还是想偷”的情况。很多指纹采集脚本是公开的特征 Hooks 已经被维护在规则列表里直接拦截要比让它跑完后抹掉结果更省力。API 混淆层这是 camofox 里最常被问到、也最容易踩坑的部分。我使用 CanvasBlocker 这类扩展对 Canvas、WebGL、AudioContext 的读取结果注入噪声。注入的噪声是随机的所以同一个页面刷新两次脚本读到的值会不一样网站就很难把两次访问关联到同一台设备上。一致性层指纹加密容易但一致性很难。很多反指纹方案只改了 User-Agent却忘了navigator.platform这就会暴露矛盾。camofox 会把语言、时区、UA、屏幕区块、字体子集放在一起考虑让所有暴露出来的特征都指向“同一个人设”。如果你的人设是“Windows 英文 欧洲中部时间”那 UA 里就不能出现 Linux 字符字体列表里也不该出现一整套 macOS 的字体。2.3 一次带状态的请求全流程我们以一个普通页面访问为例看看 camofox-browser 在中间到底做了哪些事。用户输入网址后浏览器先去请求 document。这个过程中第三方脚本可能会尝试读取navigator.userAgent、navigator.platform、screen.width等基础信息。此时偏好层已经把这些值改成了统一的人设脚本拿到的是一套“看似自洽但没有真实细节”的数据。页面继续加载追踪器试图创建 Canvas 元素来绘制隐藏文字然后读取 base64 图片数据。API 混淆层会拦截这次读取并返回一份被注入随机噪点的图像数据而且每次读取都不一样。如果脚本再去遍历字体列表会发现自己只能拿到少量预置的标准字体。最后脚本尝试写入第三方 Cookie 或者发起跨站请求内容拦截层会按规则直接拦掉即使放行了也会落在独立的容器里无法与主账号的 Cookie 互相串通。整个过程里用户不需要点击任何按钮也不需要通过任何外部工具。这也是我坚持做浏览器配置层而不是做一个普通扩展的原因配置层发生在一切脚本运行之前在入口处就把信息流拧紧了。3. 构建 camofox-browser可复现的配置、脚本和踩坑点3.1 为什么基线选 Firefox ESR构建的第一步不是写代码而是选择底座。我选了 Firefox ESR而不是普通的 Release 版。原因很现实ESR 每年只出一个大版本策略机制稳定user.js里的配置项不会像 Release 版那样每四周就变一次。对于需要长期维护的隐私配置项目来说稳定比新鲜更重要。另一个考虑是扩展签名策略。隐私保护高度依赖 CanvasBlocker、uBlock Origin 这类扩展而扩展兼容性往往跟着大版本走ESR 的节奏更适合做这类长期捆绑。如果你用普通版 Firefox可能某次自动升级后某个老扩展就被禁用了甚至 user.js 里一个参数被改名间接导致整个反指纹策略失效。我的install.sh里写了几行很关键的启动逻辑#!/usr/bin/env bash set -euo pipefail BASE_DIR$HOME/.camofox PROFILE_DIR$BASE_DIR/profile FIREFOX_BIN${FIREFOX_BIN:-firefox-esr} mkdir -p $PROFILE_DIR/extensions $PROFILE_DIR/chrome cp -r assets/profile/* $PROFILE_DIR/ $FIREFOX_BIN --profile $PROFILE_DIR --new-instance $重点解释下--new-instance这个参数。它保证 camofox-browser 会和系统里已有的 Firefox 进程完全隔离不会开一个窗口结果还是用了原来的会话。独立 Profile 是这一切的前提没有独立 Profile后面所有配置都等于白写。3.2 核心 user.js 配置逐条解读以下是 camofox-browser 里最关键的几组配置。它们不追求极端而是讲究“有效且可回退”。user_pref(privacy.resistFingerprint, true); user_pref(privacy.resistFingerprint.letterboxing, true); user_pref(privacy.resistFingerprint.autoDeclineNoUserInputCanvasPrompts, true); user_pref(privacy.trackingprotection.fingerprinting.enabled, true); user_pref(privacy.trackingprotection.cryptomining.enabled, true); user_pref(webgl.disabled, true); user_pref(media.peerconnection.enabled, false); user_pref(browser.cache.disk.enable, false); user_pref(browser.sessionstore.privacy_level, 2);先说privacy.resistFingerprint。这是一把总开关。打开后Firefox 会隐藏部分真实时区统一报告经纬度限制部分媒体信息还会让对方更容易检测到“用户在改动指纹”。它和letterboxing联动时屏幕尺寸会四舍五入到固定的档位防止用窗口大小做唯一识别。很多人以为privacy.resistFingerprint打开就万事大吉实际不是。这个开关不会自动拦截所有追踪脚本它只做“浏览器信息暴露”的收口真正扛住指纹脚本第一波攻击的是privacy.trackingprotection.fingerprinting.enabled。这一项让 Firefox 自身具备识别指纹脚本的能力能够动态拦截已知的采集行为。接下来是webgl.disabled。WebGL 指纹的熵值极高因为它依赖显卡驱动与 GPU 型号同样一张显卡在不同驱动版本下的渲染结果都可能不同稳定性极好。对于日常只浏览网页的人来说禁用 WebGL 对绝大多数站点都不会产生肉眼可见的影响最多是在地图类页面无法使用 3D 模式。这项属于典型的高收益小代价配置。media.peerconnection.enabled这个值也值得单独说。WebRTC 本意是浏览器之间的实时通信但它的第二个隐患是会暴露本地 IP 地址。即使你很小心地使用隐私模式WebRTC 仍然可能通过 STUN 请求把本地端口和 IP 反射出来。camofox 默认把它关掉。代价也很清楚网页版视频会议、在线通话功能会失效。所以完整配置里我把这一项单独抽出来放在了 optional 目录由用户自行决定是否启用。3.3 验证配置是否真的生效配置写到 user.js 之后普通人最大的误解是“保存了就等于生效了”。实际上 user.js 只在 Profile 首次启动时被读入 prefs.js如果你在浏览器运行期间修改 user.js很多项目不会热更新。我的验证方法是分三步走在地址栏打开about:config手动搜索privacy.resistFingerprint确认值是true。打开about:profiles确认当前活动 Profile 确实是我创建的 camofox Profile而不是系统默认 Profile。打开指纹检测网站连续刷新五次观察重点特征是否每次都在变化。第三步最容易发现问题。比如 Canvas hash 应该每次不同如果发现某一次又变回了真实值多半是 QuickTime 或者某个扩展绕过了配置去直接访问了底层 API。这种情况就得靠后面要说的实测环节来排。4. 指纹测试与真实数据哪些指标有效哪些指标翻车4.1 我的测试方法配置写完不等于完事。我在 Firefox 默认 Profile 和 camofox-browser 上分别跑了一组对照测试使用的指纹检测页面是 BrowserLeaks、AmiUnique以及几个主流的指纹 SDK Demo。为了保证结果可对比我每次测试前都执行了“无痕窗口 重启浏览器”这个组合排除掉本地缓存和会话残留的影响。下面是某个时间点的一组实测对比特征Firefox 默认camofox-browserUser-Agent / UA真实系统真实浏览器版本固定为人设语言和平台标识Canvas hash跨访问稳定每次访问都不同WebGL 渲染器可读取到 GPU 型号已被禁用返回空或报错系统字体列表完整字体列表仅暴露少量核心字体屏幕分辨率与窗口真实显示器分辨率和窗口位置会被修正到统一档位时区与语言真实本地时区和语言显示为人设时区与语言AudioContext 指纹稳定注入随机噪声跨访问不一致这张表里看起来所有项目都变好了但实际上我最初的版本并没有那么顺利。4.2 第一次翻车字体枚举只拦了一半我第一版配置只开了privacy.resistFingerprint和 Canvas 噪点没有单独处理字体列表。测试时我在 BrowserLeaks 上看到了一整排字体系统里装的每一款中文字体都被列得清清楚楚。那一刻很崩溃前几个特征都统一了偏偏字体这个高熵特征完全没捂住。后来排查下来原因是 Firefox 的原生反指纹逻辑对 CSSfont-face枚举的拦截并不彻底页面完全可以用几百次document.fonts.check()遍历字体是否可用。我补的方案是把 CanvasBlocker 的字体枚举保护打开同时在配置里把网页字体标准字体重置为固定集合。只有一层不够必须“本来能读到的字体数量变小”和“读到的结果被噪声化”同时生效。4.3 第二次翻车Letterboxing 让窗口体验变得像一块补丁打开 letterboxing 之后指纹确实更统一了但体验也立刻变怪。它的原理是把实际窗口尺寸取整到固定档位多出来的空间用黑边填充。比如说你的真实窗口是 1280 宽它可能会把实际内部宽度改成 1300 或 1260然后边上留一条黑边。这条黑边在某些网页下特别显眼而且窗口边缘拖拽到一半的时候会给人一种浏览器“卡住了”的错觉。后来我养成了一个习惯在 camofox Profile 里手动把窗口拖到几个常用档位比如 1366x768、1920x1080让黑边尽量少出现。虽然还是没有完全解决但至少在使用舒适度和反指纹强度之间找到了一个我能接受的平衡点。4.4 可用性损耗打工人真实版隐私是有代价的。camofox-browser 测下来以下三类场景影响最明显登录环节验证码频率明显提高特别是依赖行为分析的风控系统会因为“指纹漂移”把你判断为可疑流量。在线视频的清晰度可能下降浏览器不再告诉你确切的硬件信息播放器会选择更保守的画质档位。依赖 WebRTC 的网页会议、云手机、在线协同工具可能无法使用因为相关端口和接口已被关闭。如果你只是想给日常浏览器加一层保护这三点很可能让你想直接卸载。但如果你能接受“只在高敏感场景下使用一个专用隐私浏览器”那这些损耗就变成了一种主动选择而不是失败。5. camofox-browser 的边界和下一步它不是银弹但值得持续维护5.1 它能防什么不能防什么我个人对 camofox-browser 的期望从来不是“匿名上网”而是“减少被关联的广度”。它擅长的事情很清晰阻止第三方 Cookie 跨站追踪干扰脚本级指纹采集统一最容易暴露真实设备的信息切断已知的采集行为。它不擅长也不会去做的事也同样清晰不伪装网络出口不处理任何代理类工作不改变你的网络出口地址。所有策略都发生在浏览器这个软件实体内部它是让浏览器“少说话”而不是给你换一个新身份。这里列一个我能接受的能力边界表能力状态阻止第三方 Cookie 跨站追踪支持干扰 Canvas/WebGL 指纹采集支持统一屏幕、时区、语言、UA 等基础特征支持隔离不同站点的登录态支持防止网页获取真实的本地 IP通过 WebRTC关闭接口即可抵御恶意软件级别的键盘记录或漏洞攻击不在范围内防止登录后账号体系和身份证件信息关联不在范围内5.2 给准备抄作业的人三条建议如果你也想在自己的机器上复刻一份 camofox-browser我有三条很实际的话想说。第一不要一次整包抄走。user.js 里的每一项都可能和一个具体使用场景冲突最好的方式是一组一组地启用每一组启用后在真实网站上使用两三天确认没有影响到关键功能再继续下一组。第二为它准备一个独立 Profile 是底线。我见过太多人把隐私配置写进自己日常使用的 Firefox Profile结果某一天为了开网页视频会议被迫在about:config里改回设置改完就再也回不来了。独立 Profile 能让“隐私模式”和“日常模式”共存而且切换成本极低。第三每季度检查一次配置是否失效。Firefox 的隐私偏好项偶尔会被重新命名或废弃比如某些旧版privacy.trackingprotection系列参数就在不同版本中调整过行为。保持一个“验证项清单”即使不打开指纹测试网站也要定期看一眼关键偏好值还对不对。5.3 后续路线从配置脚本变成真正的浏览器发行配置camofox-browser 目前还停留在“个人配置 脚本”的阶段。下一步我打算做三件事一是把配置做成可以一键生成的 Profile 包降低安装门槛二是开发一个配套的 WebExtension独立承担 Canvas 噪点和字体枚举保护减少对第三方扩展的依赖三是给项目加一个“审计模式”启动后自动记录当前环境的指纹熵和特征变化并把结果输出成一份可读报告方便判断一次配置修改到底带来了多少收益。最后分享一个我自己一直保留的小技巧判断一个隐私设置有没有生效别只看网上别人写的结论直接打开about:config按名字搜再在指纹检测页面上做五次刷新测试。隐私加固这件事最大的敌人不是技术而是“我已经设置了”的错觉。camofox-browser 还在持续维护也希望你能在它的基础上改出一版真正属于你自己的 Fox。