WebRTC 指纹读取你的编解码器列表和 SDP 提议,而非你的 IP。了解其原理、与 WebRTC 泄露的区别,以及如何减少它。
WebRTC 指纹技术通过读取浏览器支持的音视频编解码器列表,以及它在协商通话时生成的 SDP(会话描述协议)文本的确切结构来识别你的浏览器——完全不涉及你的 IP 地址。脚本只需调用一个静态方法,就能拿到一份结构化的编解码器与头部扩展列表,并利用这份列表的形态来帮助区分浏览器、渲染引擎与平台。这与广为人知的 WebRTC IP 泄露 是完全不同的机制,把两者混为一谈会带来一种虚假的安全感:解决了一个,对另一个毫无帮助。
核心要点
- WebRTC 指纹技术读取的是编解码器支持情况与 SDP 结构——它从不触碰你的公网或本地 IP 地址,因此与经典的 WebRTC 泄露毫无关系。
- 核心信号来自
RTCRtpSender.getCapabilities(),这是一个静态方法,无需任何权限弹窗、也不产生任何网络流量即可返回你支持的编解码器。 createOffer()生成的 SDP 文本又增加了一层信号:负载类型编号、编解码器排列顺序,以及诸如profile-level-id或packetization-mode这样的逐编解码器参数。- 单看这个信号相当粗糙——它主要区分的是浏览器引擎、版本和平台,而非唯一识别某一个人——但它会作为叠加层,与 Canvas、WebGL 和音频指纹一起发挥作用。
- 彻底禁用 WebRTC 能移除这个暴露面;随时间推移,让数值发生变化的主要是浏览器更新以及操作系统层面的硬件编解码支持。
什么是 WebRTC 指纹技术?
每一个实现了 WebRTC 的浏览器都会携带一套特定的音视频编解码器,按特定的顺序排列,并带有特定的参数。基于 Chromium 的浏览器、Firefox 和 Safari 各自基于不同的底层媒体栈构建,因此确切的编解码器列表——以及某个操作系统上哪些编解码器能获得硬件加速——在它们之间各不相同。脚本可以直接读取这份列表,或者促使浏览器生成一份 SDP 提议,并将其中任意一种用作设备/浏览器信号。
关键在于,这与 WebRTC IP 泄露 并不属于同一类风险——在 IP 泄露中,ICE/STUN 过程可能在你使用 VPN 时仍然暴露你的真实公网 IP。而编解码器与 SDP 指纹揭示的是你的浏览器是什么,而不是你在哪里。你完全可以做到零 WebRTC 泄露(一个配置完善、全程走代理的 VPN),却依然暴露出一份再普通不过的编解码器指纹,反之亦然。
无需建立连接即可读取编解码器能力
最简单、最不动声色的技术甚至不需要创建一个 peer connection。RTCRtpSender.getCapabilities(kind) 是一个静态方法——它会立即返回浏览器支持的编解码器与头部扩展,没有权限弹窗、没有信令、也没有任何网络活动:
function getWebRTCCodecFingerprint() {
if (!window.RTCRtpSender || !RTCRtpSender.getCapabilities) {
return { supported: false };
}
const audio = RTCRtpSender.getCapabilities('audio');
const video = RTCRtpSender.getCapabilities('video');
return {
supported: true,
audioCodecs: audio.codecs.map((c) => c.mimeType),
videoCodecs: video.codecs.map(
(c) => `${c.mimeType}${c.sdpFmtpLine ? ' ' + c.sdpFmtpLine : ''}`
),
headerExtensions: video.headerExtensions.map((h) => h.uri),
};
}
返回的 codecs 数组包含每个编解码器的 MIME 类型、时钟频率、声道数,以及一个可选的 sdpFmtpLine,其中携带编解码器专属的参数。headerExtensions 数组则列出了受支持的 RTP 头部扩展(用于音量等级、传输级拥塞控制等用途),它们的有无及排列顺序同样会因引擎和版本而异。
SDP 提议中包含什么
如果脚本更进一步,实际调用了 createOffer(),生成的 SDP 文本会以更细粒度的形式暴露同样的信息:负载类型编号、编解码器排序,以及逐编解码器的属性行。下面是一段经过精简、仅作示意的视频部分:
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 102 127
a=rtpmap:96 VP8/90000
a=rtpmap:98 VP9/90000
a=rtpmap:102 H264/90000
a=fmtp:102 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
a=rtpmap:127 AV1/90000
SDP 格式本身是一份标准化的 IETF 文本协议——真正有趣的指纹信号并非语法本身,而是你这台特定浏览器/系统/版本所选择呈现的负载类型编号、编解码器顺序与 fmtp 参数的具体组合。不同平台上的两个浏览器,很少会完全重现同一种组合。
这与 WebRTC IP 泄露有何不同
有必要把这条边界说清楚,因为这两种技术共享同一套代码库(WebRTC),但除此之外没有任何共同点:
| WebRTC IP 泄露 | WebRTC 编解码器/SDP 指纹 | |
|---|---|---|
| 暴露了什么 | 你的真实公网/本地 IP 地址 | 你的浏览器引擎、版本与平台 |
| 机制 | 通过 STUN/TURN 进行 ICE 候选者收集 | getCapabilities() 和/或 SDP 提议内容 |
| 是否需要网络往返? | 是——需要与 STUN 服务器通信 | 否——getCapabilities() 完全在本地完成 |
| 会绕过 VPN 吗? | 如果路由在隧道之外泄露,则可能 | 不适用——不涉及任何位置数据 |
| 修复方式 | 限制/禁用 WebRTC,或使用不泄露的 VPN | 禁用 WebRTC,或减少编解码器暴露面 |
如果你已经堵住了 WebRTC 泄露,那保护的是你的位置——它对编解码器/SDP 信号毫无作用,因为 getCapabilities() 从一开始就不会请求任何网络访问权限。
这个信号的区分度有多高?
编解码器与 SDP 指纹相对而言是一种较为粗糙的信号。由于 Chrome、Edge、Opera 和 Brave 都基于同一套 Chromium/libwebrtc 基础构建,它们上报的编解码器列表往往非常相似——这个信号主要区分的是引擎家族(Chromium vs. Gecko vs. WebKit)与平台(操作系统向浏览器暴露了哪些硬件解码器),而不是精确锁定某一个具体的人。
| 因素 | 揭示了什么 |
|---|---|
| 编解码器列表与顺序 | 渲染引擎家族与版本 |
sdpFmtpLine 参数(如 H.264 的 profile-level-id) | 平台媒体栈与硬件解码器支持 |
| 头部扩展列表 | 引擎版本,有时还包括已启用的实验性特性 |
| 是否支持 AV1/HEVC | 操作系统与硬件加速能力 |
这使它只能算是一个适度的贡献者——就像 音频指纹 一样:单靠它不足以识别出你,但它是另一个独立的比特贡献者,会与 Canvas 和 WebGL 信号叠加,进一步缩小人群范围。
编解码器之外的媒体能力
还有一个值得了解的相关但独立的 API:媒体能力 API(Media Capabilities API),具体来说是 navigator.mediaCapabilities.decodingInfo()。它并不属于 WebRTC 的一部分,但常常与 WebRTC 一起被探测,因为它回答的是一个类似的问题——这台设备的媒体管线到底能做什么——具体做法是报告解码某个编解码器/分辨率/帧率组合是否会是 smooth(流畅)和 powerEfficient(省电,即获得硬件加速)。由于硬件加速支持与具体的 GPU 和操作系统媒体框架绑定,这在 WebRTC 暴露的软件编解码器列表之上,又叠加了一层带有硬件色彩的信号。
防护手段及其取舍
| 方法 | 效果 | 取舍 |
|---|---|---|
禁用 WebRTC(Firefox 的 media.peerconnection.enabled) | 同时移除编解码器/SDP 信号与 IP 泄露暴露面 | 会彻底破坏浏览器内的视频/语音通话 |
| Tor 浏览器 | 默认禁用 WebRTC | 同样会失去这部分功能,但在所有 Tor 用户之间保持一致 |
| 通过扩展限制为单一编解码器集合 | 可以减少上报的编解码器 | 很少有主流扩展专门针对这一信号,大多数扩展关注的是 IP 泄露 |
| 接受这个信号,把精力放在别处 | 不会损失任何功能 | 对大多数威胁模型而言,相比 Canvas/WebGL,编解码器指纹是价值较低的目标 |
目前并没有一个像 Canvas 或音频「farbling」那样、专门用来「随机化我的编解码器列表」的主流开关,主要是因为这个信号本身就比那些信号更粗糙、价值更低。对大多数人来说,实际的做法是把它当作叠加在高熵信号之上的又一项输入,而不是单独为它较劲的战场。
如何检查你自己的信号
无需编写任何代码,你就可以看到自己浏览器暴露了什么:
- 运行 BrowserInsight 的 指纹检测,查看你的综合指纹画像,包括像这类低熵信号在你整体独特性中所占的比重。
- 打开浏览器的开发者控制台,运行上面的
getWebRTCCodecFingerprint()代码片段,查看你的原始编解码器列表。 - 在同一台机器上,把这个测试分别放到 Chrome、Firefox 和 Safari 中运行对比——即便硬件完全相同,不同引擎之间的编解码器顺序和
fmtp参数通常也会有所不同。EFF 的 Cover Your Tracks 项目也是一个很好的补充检测,可以看看你整体的配置相较他人如何。
常见问题
WebRTC 指纹会暴露我的 IP 地址吗?
不会。编解码器与 SDP 指纹读取的是你的浏览器支持什么,使用的是 RTCRtpSender.getCapabilities() 或者 SDP 提议的内容——两者都不涉及你的网络地址。IP 暴露是一种独立的机制(即 WebRTC 泄露),在我们的 WebRTC 泄露防护指南 中有介绍。
禁用 WebRTC 能同时解决泄露和指纹问题吗?
可以——彻底禁用 WebRTC(或者使用像 Tor 这样默认禁用它的浏览器),能一次性移除这两个暴露面,因为无论是 getCapabilities() 还是 SDP 协商,在 WebRTC 未启用的情况下都无法运行。代价是浏览器内的视频/语音通话功能会随之失效。
编解码器指纹和 Canvas 或 WebGL 指纹一样具有识别性吗?
不,它通常要弱一些。由于主流的基于 Chromium 的浏览器共享同一套底层 libwebrtc 技术栈,它们的编解码器列表看起来很相似,因此这个信号主要揭示的是引擎家族和平台,而非精确锁定某个个体。它仍然会为整体的熵值叠加做出贡献,只是不如基于渲染的信号那么多。
网站能在未经许可的情况下读取我的编解码器列表吗?
可以。RTCRtpSender.getCapabilities() 是一个静态方法,运行时不会弹出摄像头/麦克风权限提示,也不会发起任何网络请求——它只是单纯上报浏览器的 WebRTC 实现所支持的内容。
总结
WebRTC 指纹技术和 WebRTC IP 泄露都寄居在同一个 API 之内,但它们暴露的是完全不同的东西:一个揭示你真实的网络地址,另一个则通过编解码器支持和 SDP 结构揭示你的浏览器引擎、版本与平台。二者互不能替代。单独来看,编解码器/SDP 指纹只是一个适度、粗粒度的信号——但理解它意味着你不会把一套堵住泄露的 VPN 配置,误当成一套对指纹也免疫的配置。
推荐阅读:


