什么是浏览器指纹
浏览器指纹(Browser Fingerprinting),是一种通过收集用户浏览器和设备特征来识别用户的技术。
网站的运营者需要识别和跟踪,到底是谁在访问网站。通常用用于以下几种场景:
- 广告跟踪:比如识别到你搜索某个关键词、浏览某些内容的网页,可以精准给你推送相关的广告。
- 安全防护:当同一账户,频繁从不同设备、不同的浏览器环境登录时,可能账户存在风险。
- 防欺诈:防止同一个设备重复注册、作弊、多开账户,减少虚假账号的产生。
通过loaclStorage/Cookie等本地存储方案,无法满足识别用户身份的需求。因为:
- 本地存储受到同源策略的限制,难以跨网站跟踪用户。
- 用户可以很方便地删除或编辑 Cookie,或者使用隐私浏览模式。
为了实现更精准、更泛用地识别用户的设备和浏览器信息,开发人员利用多种JavaScript/HTML API,判断用户的IP、硬件、操作系统、浏览器信息等等,用这些信息来识别具体的用户。这样,即便用户访问不同的网站、删除cookie或使用隐私浏览模式,网站仍然能够识别出他们。
每个用户的设备、浏览器配置、使用习惯可能都不尽相同,这些信息的组合往往是独一无二的,类似类似于人类的指纹。因此,这些信息的组合就被称为“浏览器指纹”。
如何采集浏览器指纹
有很多网站可以在线识别浏览器指纹,如:
- amiunique.org
- browserscan.net
如果需要在项目中使用,也可以使用开源的依赖库 fingerprintjs 。访问官方Demo https://fingerprintjs.github.io/fingerprintjs ,可以查看到当前的浏览器指纹。
通过上述方案识别浏览器指纹,我们可以看到浏览器指纹包含以下信息:
- IP地址 Http请求头会带有IP,可以在服务端获取当前请求的IP。 如果用户使用了代理,服务器只能拿到代理的ip。此时可以使用WebRTC的RTCPeerConnection对象来创建一个点对点连接,然后通过监听ICE(Interactive Connectivity Establishment)候选项,可以获取局域网和公网的IP地址。现代浏览器已经对WebRTC做了改进,默认阻止了IP地址泄露。
- UserAgent Http请求头带有“User-Agent”字段。如果请求头刻意伪造了UserAgent,还可以通过navigator.userAgent来拿到真实的UserAgent。 UserAgent中包含了 操作系统及其版本、浏览器名称及其版本、设备类型、渲染引擎 等信息。
- 地理位置 虽然可以通过Geolocation API拿到用户地理位置,但这个操作需要用户授权同意,通常会影响用户体验、侵犯用户隐私。因此通常使用的是IP地址识别的方案来确定用户的地理位置。
- 语言 通过Http请求头的“Accept-Language"字段,或者navigator.language和navigator.languages,可以获取语言信息。
- 时区 使用 new Date().getTimezoneOffset() 获取当前浏览器的时区。获取到的是用户设备所设置的时区,如果这个时区和上述地理位置的时区有差异,那需要注意用户是否使用了代理。
- 屏幕分辨率 使用 window.screen、window.devicePixelRatio、window.innerWidth和 window.innerHeight等API获取设备和屏幕的分辨率。甚至,如果用户禁用了JavaScript,依然可以通过CSS的媒体查询,根据用户加载了哪些资源判断用户的屏幕分辨率,例如: @media(device-width: 1080px) { body { background: url("https://example.org/1080.png"); } }
- 硬件设备 使用navigator.hardwareConcurrency获取处理器数量,使用navigator.deviceMemory获取内存大小。为了保护用户隐私,即使真实的内存大小超过了8GB,浏览器返回的内存大小也只是8GB。 对于移动设备,还可以使用navigator.getBattery()获取电池信息,Web Bluetooth API获取蓝牙信息等方式,作为指纹信息的一部分。
- 媒体设备 MediaDevices API 是一个可以访问用户媒体设备(如摄像头、麦克风等)的API,通常用来获取音频和视频流。可以使用 navigator.mediaDevices.enumerateDevices() 来获取用户可用的媒体设备(如摄像头、麦克风、扬声器等)列表。这个方法不会直接访问设备的功能(如启动摄像头),因此在获取设备信息时不需要用户的授权。 但是,如果想进一步获取用户的硬件设备信息(比如设备的ID、类型、标签),就需要用户的同意授权。
- 字体 通过FontFaceSet API可获取字体。现代浏览器为了保护用户隐私,不能列出所有的字体列表,但可以通过创建隐藏元素并比较字体渲染差异,来检测用户是否安装了某个字体。
- Canvas 利用 Canvas API 绘制一些图形或文本,然后将其转换为图像数据(或哈希值)。由于硬件、字体渲染、图形处理等原因,不同的设备对抗锯齿、次像素渲染等算法也不同,从而会生成不同的图像数据,可以以此来作为指纹信息之一。
- WebGL和WebGPU WebGL 是一种允许网页使用 JavaScript 在浏览器中直接调用 GPU 渲染 3D 图形的 API。由于不同设备的 GPU、驱动程序、操作系统和浏览器对 WebGL 的实现存在细微差异。通过在屏幕显示区域以外渲染复杂的图形,根据生成的图像做哈希值,同样可以作为指纹信息之一。
- ClientRects getClientRects()返回一个包含 DOM 元素子元素(如行内元素、文本块等)所占用矩形区域的列表(ClientRectList); getBoundingClientRect()返回一个 DOMRect 对象,表示元素相对于屏幕的边界框。不同浏览器、操作系统、缩放设置以及字体渲染引擎可能会导致同一元素的 ClientRect 数值有所不同,因此这些细微的差异可以作为指纹信息之一。
- AudioContext Web Audio API 是一个高级 API,用于处理和分析音频。通过调用 AudioContext 来生成指纹,通常涉及创建一个简单的音频信号(如正弦波),然后通过音频节点进行处理和分析。由于不同设备、浏览器和音频硬件在处理同一音频信号时会有微小的差异,尤其在浮点计算和音频滤波时,这些差异就可以用来生成独特的音频指纹。
- SpeechVoices SpeechSynthesis API 提供了语音合成功能,允许开发者访问设备上可用的语音(voices),并为用户生成语音。通过 SpeechSynthesis.getVoices() 方法可以获取用户设备上可用的语音列表,可以作为指纹信息之一。
如何修改浏览器指纹
浏览器指纹技术被广泛批评,因为它通常是在用户不知情的情况下进行的,并且很难通过常规手段阻止,因此被认为侵犯了用户隐私。但是,了解到原理以后,每一个指纹信息都能通过技术手段修改和伪造。
目前行业主流的思路有两种:1、开发一个用于修改指纹的浏览器插件。2、开发一个用于修改指纹的浏览器。下面我们分别讨论一下两种技术方案的优劣。
指纹浏览器插件
优点:
- 开发成本低,只需要编写简单的脚本即可实现一个浏览器插件。
- 维护成本低,浏览器版本升级不影响插件使用。
- 不需要用户更换浏览器,不需要用户更改使用习惯。
缺点:
- 插件需要用户授予特定权限,如访问代理设置、修改HTTP请求头等。
- 受限于浏览器的安全策略,无法进行一些低级别的系统操作。
指纹浏览器
优点:
- 完全控制浏览器的所有行为和数据,可以实现更高级别的指纹保护和修改,理论上可以覆盖所有可能的指纹信息。
- 不受限于浏览器的安全策略,可以实现更复杂的功能。
- 可以整合指纹以外的其他功能,比如多开、自动化等。
缺点:
- 用户需要下载和安装一个新的浏览器,可能会影响用户的使用习惯。
- 开发难度较大,需要掌握浏览器内核的相关知识。
- 有些指纹浏览器还实现了可以切换Chromium/Firefox内核版本的功能。随着浏览器内核升级迭代,也会产生较大的维护和适配的工作量。
道高一尺魔高一丈,用于生成指纹的信息也在不断增加,需要持续投入精力去更新、完善新的指纹信息修改方式。因此,目前市面上很多指纹浏览器插件和指纹浏览器都是收费的。
本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。