浏览器指纹追踪-溯源防范

Z简介

浏览器指纹是指通过收集浏览器和计算机的特定信息,如用户代理字符串、操作系统、屏幕分辨率、安装的字体、插件和扩展等数据,来唯一识别和跟踪用户的一种技术。由于这些信息的组合是唯一的,因此可以用于识别和追踪用户的在线活动,即使用户已经启用了隐私保护措施,例如使用虚拟专用网络(VPN)或匿名浏览器模式。

浏览器指纹技术通常被用于广告追踪、用户行为分析和反欺诈等领域。虽然浏览器指纹不能提供用户的真实身份信息,但它可以收集关于用户的足够多的信息,以便追踪他们的在线活动并建立详细的用户画像。因此,浏览器指纹可能会对用户的隐私和安全构成潜在威胁,需要采取一些技术手段来保护用户隐私。

在浏览器与网站服务器交互时,浏览器会向网站暴露许多的不同消息,比如浏览器型号、浏览器版本、操作系统等信息。如同人的指纹可以用来识别不同的人一样,当浏览器暴露信息的熵足够高时,网站就可利用这些信息来识别、追踪和定位用户

 

浏览器指纹可以被用于跟踪用户的在线活动,这可能会侵犯用户的隐私和安全。以下是浏览器指纹可能带来的一些危害:

隐私侵犯:浏览器指纹可以被用于识别用户和跟踪他们的在线活动,这可能泄露用户的个人信息和隐私。

跨站点追踪:浏览器指纹可以被用于跨站点追踪,这意味着即使用户在不同的网站上使用不同的用户名和密码,他们的在线活动仍然可以被追踪和关联。

安全威胁:浏览器指纹可以被用于攻击用户的设备,例如欺骗用户访问恶意网站或下载恶意软件。

降低用户体验:浏览器指纹可以被用于限制用户的访问,例如限制用户访问某些网站或服务。

因此,为了保护用户的隐私和安全,现代浏览器通常会提供一些隐私保护功能,如阻止跨站点追踪和限制指纹的收集和使用等。

许多大站 电商 第三方平台 广告商 都会获取浏览器指纹 进行分析和数据共享 这种利用浏览器指纹追踪的手段 挂了VPN 还是SS 还是隐藏了WebRTC 只要没换浏览器 都太大用

可谓是溯源之利器 不过相对而言成本会高一些 要先收集很多数据才能进行有效分析

 

浏览器指纹技术可以用于数据分析,但也存在一些问题,包括以下几点:

数据收集的误差问题:由于浏览器指纹技术可能会误认为不同的用户是同一人,或将同一用户的不同浏览器指纹认为是不同的用户,导致数据采集的误差。

数据质量问题:浏览器指纹技术所获取的数据可能存在一定的噪音,例如不同的机器、网络、地理位置等因素可能会影响浏览器指纹的准确性,从而影响数据的质量。

数据隐私问题:浏览器指纹技术可以收集用户的个人信息,包括操作系统、浏览器版本、插件和扩展等,可能会侵犯用户的隐私。

数据分析的误导性问题:由于浏览器指纹技术只能提供浏览器和计算机的特定信息,无法提供用户的真实身份信息,因此数据分析结果可能存在一定的误导性,例如将同一用户的不同浏览器指纹认为是不同的用户,导致数据分析结果的准确性降低。

浏览器基本指纹

基本指纹是任何浏览器都具有的特征标识,比如硬件类型(Apple)、操作系统(Mac OS)、用户代理(User agent)、系统字体、语言、屏幕分辨率、浏览器插件 (Flash, Silverlight, Java, etc)、浏览器扩展、浏览器设置 (Do-Not-Track, etc)、时区差(Browser GMT Offset)等众多信息,这些指纹信息“类似”人类的身高、年龄等,有很大的冲突概率,只能作为辅助识别。

浏览器特征指纹获取:

指纹内容 获取指纹的方式
userAgent(用户代理) navigator.userAgent
浏览器的语言 navigator.language
浏览器的插件 Array.from(navigatorObj.plugins).map(item => item.name).join(‘,’)

系统特征指纹获取:

指纹内容 获取指纹的方式
操作系统 navigator.platform

时区特征指纹获取:

指纹内容 获取指纹的方式
格林威治时间和本地时间之间的时差 new Date().getTimezoneOffset()
时区所属 需查询服务器获取相应信息
地区经纬度 navigator.geolocation.getCurrentPosition(需在https安全环境下才能调用)或查询服务器获取相应信息
地理地区名称 需查询服务器获取相应信息
IP地址 需查询服务器获取相应信息

硬件特征指纹获取:

指纹内容 获取指纹的方式
设备能够支持的最大同时触摸的点数 navigator.maxTouchPoints
可用的逻辑处理器核心数 navigator.hardwareConcurrency
设备屏幕的宽高与色彩信息 ${screen.width}*${screen.height}*${screen.colorDepth}

浏览器高级指纹

普通指纹是不够区分独特的个人,这时就需要高级指纹,将范围进一步缩小,甚至生成一个独一无二的跨浏览器身份。用于生产指纹的各个信息,有权重大小之分,信息熵大的将拥有较大的权重。

Canvas 指纹

Canvas是HTML5中的动态绘图标签,也可以用它生成图片或者处理图片。即便使用Canvas绘制相同的元素,但是由于系统的差别,字体渲染引擎不同,对抗锯齿、次像素渲染等算法也不同,canvas将同样的文字转成图片,得到的结果也是不同的。

由于指纹主要基于浏览器、操作系统和已安装的图形硬件,因此还是无法完全做到唯一识别用户(重复率很低)。

实现代码大致为:在画布上渲染一些文字,再用 toDataURL 转换出来,即便开启了隐私模式一样可以拿到相同的值。


1
2
3
4
5
6
7
8
9
function getCanvasFingerprint () {
var canvas = document.createElement('canvas');
var context = canvas.getContext("2d");
context.font = "18pt Arial";
context.textBaseline = "top";
context.fillText("Hello, user.", 2, 2);
return canvas.toDataURL("image/jpeg");
}
getCanvasFingerprint()

接着将获取到的canvasImageData通过hash算法得出唯一值(md5或者sha256等),就是我们所得到的canvas指纹了:

import sha256 from 'crypto-js/sha256';
const canvasFinger = sha256(canvasImageData)

WebGL 指纹

WebGL(Web图形库)是一个 JavaScript API,可在任何兼容的 Web 浏览器中渲染高性能的交互式 3D 和 2D 图形,而无需使用插件。WebGL 通过引入一个与 OpenGL ES 2.0 非常一致的 API 来做到这一点,该 API 可以在 HTML5 元素中使用。这种一致性使 API 可以利用用户设备提供的硬件图形加速。网站可以利用 WebGL 来识别设备指纹,一般可以用两种方式来做到指纹生产:

  • WebGL 报告,完整的 WebGL 浏览器报告表是可获取、可被检测的。在一些情况下,它会被转换成为哈希值以便更快地进行分析。
  • WebGL 图像,渲染和转换为哈希值的隐藏 3D 图像。由于最终结果取决于进行计算的硬件设备,因此此方法会为设备及其驱动程序的不同组合生成唯一值。这种方式为不同的设备组合和驱动程序生成了唯一值。

产生WebGL指纹原理是首先需要用着色器(shaders)绘制一个梯度对象,并将这个图片转换为Base64字符串。然后枚举WebGL所有的拓展和功能,并将他们添加到Base64字符串上,从而产生一个巨大的字符串,这个字符串在每台设备上可能是非常独特的。

例如fingerprint2js库的 WebGL 指纹生产方式:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
gl = getWebglCanvas()    
if (!gl) { return null }    
var result = []    
var vShaderTemplate = 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}'
var fShaderTemplate = 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}'
var vertexPosBuffer = gl.createBuffer()    
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer)    
var vertices = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.732134444, 0])
// 创建并初始化了Buffer对象的数据存储区。
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
vertexPosBuffer.itemSize = 3
vertexPosBuffer.numItems = 3
// 创建和初始化一个WebGLProgram对象。
var program = gl.createProgram()
// 创建着色器对象
var vshader = gl.createShader(gl.VERTEX_SHADER)
// 下两行配置着色器
gl.shaderSource(vshader, vShaderTemplate)  // 设置着色器代码  
gl.compileShader(vshader) // 编译一个着色器,以便被WebGLProgram对象所使用

var fshader = gl.createShader(gl.FRAGMENT_SHADER)  
gl.shaderSource(fshader, fShaderTemplate)    
gl.compileShader(fshader)    
// 添加预先定义好的顶点着色器和片段着色器  
gl.attachShader(program, vshader)
gl.attachShader(program, fshader)
// 链接WebGLProgram对象  
gl.linkProgram(program)
// 定义好的WebGLProgram对象添加到当前的渲染状态  
gl.useProgram(program)    
program.vertexPosAttrib = gl.getAttribLocation(program, 'attrVertex')    
program.offsetUniform = gl.getUniformLocation(program, 'uniformOffset')                           gl.enableVertexAttribArray(program.vertexPosArray)    
gl.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, gl.FLOAT, !1, 0, 0)    
gl.uniform2f(program.offsetUniform, 1, 1)
// 从向量数组中绘制图元  
gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems)    
try {        
    result.push(gl.canvas.toDataURL())    
} catch (e) {        
    /* .toDataURL may be absent or broken (blocked by extension) */
}

 

音频指纹

AudioContext指纹和Canvas类似也是基于硬件设备或者软件的差别,来产生不同的音频输出,然后计算得到不同的hash来作为标志。

  • 方法一:生成音频信息流(三角波),对其进行FFT变换,计算SHA值作为指纹。
  • 方法二:生成音频信息流(正弦波),进行动态压缩处理,计算MD5值。

两种方法都是在音频输出到音频设备之前进行清除,用户根本就毫无察觉就被获取了指纹。

以fingerprintjs2的音频指纹源码为例:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
var each = function(obj, iterator) {
  if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
      obj.forEach(iterator)
  } else if (obj.length === +obj.length) {
      for (var i = 0, l = obj.length; i < l; i++) {
          iterator(obj[i], i, obj)
      }
  } else {
      for (var key in obj) {
          if (obj.hasOwnProperty(key)) {
              iterator(obj[key], key, obj)
          }
      }
  }
}

var AudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext

var context = new AudioContext(1, 44100, 44100)

var oscillator = context.createOscillator()
oscillator.type = 'triangle'
oscillator.frequency.setValueAtTime(10000, context.currentTime)

var compressor = context.createDynamicsCompressor()
each([
  ['threshold', -50],
  ['knee', 40],
  ['ratio', 12],
  ['reduction', -20],
  ['attack', 0],
  ['release', 0.25]
], function (item) {
  if (compressor[item[0]] !== undefined && typeof compressor[item[0]].setValueAtTime === 'function') {
    compressor[item[0]].setValueAtTime(item[1], context.currentTime)
  }
})

oscillator.connect(compressor)
compressor.connect(context.destination)
oscillator.start(0)
context.startRendering()

var audioTimeoutId = setTimeout(function () {
  console.warn('Audio fingerprint timed out. Please report bug at https://github.com/Valve/fingerprintjs2 with your user agent: "' + navigator.userAgent + '".')
  context.oncomplete = function () { }
  context = null
  return done('audioTimeout')
}, 100)

context.oncomplete = (event) => {
  try {
    clearTimeout(audioTimeoutId)
    audioFingerprint.value = event.renderedBuffer.getChannelData(0).slice(4500, 5000)
      .reduce(function (acc, val) { return acc + Math.abs(val) }, 0)
      .toString()
    oscillator.disconnect()
    compressor.disconnect()
    audioFingerprintHash.value = sha256(audioFingerprint.value)
  } catch (error) {
    console.log(error)
    return
  }
}

 

WebRTC

WebRTC(网页实时通信,Web Real Time Communication),是可以让浏览器有音视频实时通信的能力,它提供了三个主要的API来让JS可以实时获取和交换音视频数据,MediaStream、RTCPeerConnection和RTCDataChannel。当然如果要使用WebRTC获得通信能力,用户的真实ip就得暴露出来(NAT穿透),所以RTCPeerConnection就提供了这样的API,直接使用JS就可以拿到用户的IP地址。

指纹计算

除开从http中拿到的指纹,也可以通过其他方式拿到浏览器的特性信息,在(https://panopticlick.eff.org/about)这篇文档中就陈列了一些可行的特征值

  • 每个浏览器的用户代理字符串
  • 浏览器发送的HTTP ACCEPT标头
  • 屏幕分辨率和色彩深度
  • 系统设置为时区
  • 浏览器中安装的浏览器扩展/插件,例如Quicktime,Flash,Java或Acrobat,以及这些插件的版本
  • 计算机上安装的字体,由Flash或Java报告。
  • 浏览器是否执行JavaScript脚本
  • 浏览器是否能种下各种cookie和“超级cookie(super cookies)”
  • 通过Canvas指纹生成的图像的哈希
  • WebGL指纹生成的图像的哈希
  • 是否浏览器设置为“Do Not Track”
  • 系统平台(例如Win32,Linux x86)
  • 系统语言(例如,cn,en-US)
  • 浏览器是否支持触摸屏

拿到这些值后可以进行一些运算,得到浏览器指纹具体的信息熵以及浏览器的uuid。

下图是数个特征值的信息熵、重复概率和具体的值:

 

将上面的指纹信息综合起来,可以大大降低碰撞率,提高客户端uuid的准确性。指纹的也有权重之分,在论文《Cross-Browser Fingerprinting via OS and Hardware Level Features》中更是详细研究了各个指标的信息熵和稳定性。

 

从该论文中可以看出,时区、屏幕分辨率和色深、Canvas、webGL 的信息熵在跨浏览器指纹上的权重是比较大的。

跨浏览器指纹

综上提到的浏览器指纹都是从同一个浏览器上获得。但是很多特征值都是不稳定的,例如UA、canvas指纹在相同设备的不同浏览器打开会完全不一样。同一套浏览器指纹算法在不同浏览器上也就不可用了。

跨浏览器指纹就是即便是在不同浏览器上也可以取得相同或者近似值的稳定浏览器特征。

列举了浏览器特征值的在单浏览器和跨浏览器的信息熵以及稳定性,上诉说到的canvas指纹稳定性仅有8.17%。

常规的特征值很难满足在信息量足够的情况下还保持高稳定性。

挑选几个表中符合这些特征的值Task(a)~Task(r)、List of fonts(JS)、TimeZone和CPU Vritual cores、

  • Task(a)~Task(r),它是一种显卡渲染(Rendering Tasks)图片的特征值。例如Task(a)Texture,它是测试常规片段着色器中的纹理功能,通过渲染一个随机的三基色值的像素,片段着色器需要在纹理中插入点,以便将纹理映射到模型上的每个点,这个插入算法在不同的显卡又是不一致的。如果纹理变化较大,那么差异也就越明显,我们可以通过记录这种差异来为这个显卡作出区分度。
  • List of fonts(JS),通过js获取页面支持的字体情况。获取页面支持的字体分为两种方式,Flash和JS,现在Flash渐渐退出了舞台就不考虑它了。List of fonts是值通过js拿到页面支持的字体情况以及如何绘制字体,是通过测量不同字体的文本HTML元素的填充尺寸,来和其他设备做区分。
  • TimeZone,时区,这个比较好理解,既然是同一台设备那么时区应该也是一致的。
  • CUP Vritual cores即为CPU的内核数量,最简单的方法就是通过一个hardwareConcurrency来拿到。

尽管在低版本浏览器是不支持这个API的,但也可以通过这个polyfill拿到。实现原理大致为借用Web Worker的能力,监听payload的时间,计算量达到硬件最大并发的时候就可以得到内核的数量(有点硬核)。

防范


修改浏览器设置,然后配合浏览器插件进行操作
伪造 Canvas、webGL等指纹 禁用WebRTC 修改浏览器 UA 和语言 屏蔽JS追踪器
定时清理浏览器缓存和cookie

防护措施

  1. 使用隐身模式浏览网页,缺点为无法保存历史记录。
  2. 禁用 Cookie 和 JavaScript,缺点为很有可能导致页面显示不正常,不建议。
  3. 禁用 WebRTC。Firefox 浏览器:打开 about:config,找到 media.peerconnection.enabled 的项,设置成 false。Chrome 浏览器需要使用插件禁用。
  4. 禁用 Geolocation。Firefox 浏览器:打开 about:config,找到 geo.enabled 的值,设置其值为 false。Chrome 点击设置(Settings),找到隐私和安全(Privacy and security)点击网站设置(Site Settings)在窗口里找到定位(Location)并设置不允许任何网站追踪你的物理位置(Do not allow any site to track your physical location)
  5. 使用 99% 安全的匿名浏览器,比如 Tor 洋葱浏览器,或者专业的指纹浏览器(收费)
  6. 禁用 Cookies 追踪,浏览器退出清空cookie和缓存
  7. 安装插件
    1. Ghostery 可以有效屏蔽网页追踪、广告,加速网页:https://chrome.google.com/webstore/detail/ghostery-%E2%80%93-privacy-ad-blo/mlomiejdfkolichcflejclcbmpeaniij?utm_source=chrome-ntp-icon
    2. Privacy Badger 同样可以有效屏蔽隐秘追踪:https://chrome.google.com/webstore/detail/privacy-badger/pkehgijcmpdhfbdbbnkijodmdjhbjlgp?utm_source=chrome-ntp-icon
      可以在插件设置中开启 Prevent WebRTC from leaking local IP address (防止 WebRTC 追踪泄露 IP 地址)
    3. uMatrix  十分强大的分类管理 cookie,css,image,media,script,XHR,frame 和其他:https://chrome.google.com/webstore/detail/umatrix/ogfcmafjalglgifnmanfmnieipoejdcf?utm_source=chrome-ntp-icon
      第一行和第一列的标题方块有上下两个可点击的地方,上面是开启和关闭此列 / 行全部,下面是仅开启和关闭这个主网页的,除主页面以外此网页上的其他网站等信息由上面的小方块控制。你也可以分别控制每个网页的具体单个选项。
      在软件使用的时候,除了基本上必须开启的 css 和 image 以外,大多数时候需要开启 Scrpit 选项来保证页面功能能正常运作。你也可以点击 ALL 直接允许所有都开启。
    4. CanvasBlocker  一款火狐插件,此插件允许用户阻止网站使用某些Javascript API对其进行指纹识别。用户可以选择完全阻止部分或所有网站上的API(这可能会破坏某些网站),或者只是阻止或伪造其指纹友好的读出API(https://github.com/kkapsner/CanvasBlocker)
    5. User-Agent Switcher  一款火狐插件,可以设置浏览器UA和定时随机UA
    6. Quick Accept-Language Switcher 一款火狐插件,可以设置浏览器 Accept-Language
    7. WebRTC Leak Shield  禁用WebRTC的插件
    8. uBlock Origin 过滤一些第三方请求追踪 变相禁用了WebRTC
    9. NoScript 禁用JS的插件 威力巨大 谨慎使用
    10. Canvas Fingerprint Defender 禁用Canvas  (https://chrome.google.com/webstore/detail/canvas-fingerprint-defend/lanfdkkpgfjfdikkncbnojekcppdebfp)
    11. Privacy Badger 屏蔽了广告和网页追踪器(https://chrome.google.com/webstore/detail/privacy-badger/pkehgijcmpdhfbdbbnkijodmdjhbjlgp)
    12. Random Agent Spoofer 一款随机浏览器UA的插件(https://chrome.google.com/webstore/detail/random-user-agent-switche/einpaelgookohagofgnnkcfjbkkgepnp)
    13. Vytal 可以隐藏时区、区域设置、地理位置和用户代理等这些可以用来追踪或泄露位置的数据信息 chrome插件(https://github.com/vytal-io/vytal-extension)

    检测网站

    https://uutool.cn/browser/

    https://www.yalala.com/

     

     


     

    版权声明:
    作者:agsec
    链接:https://agsec.xyz/archives/173
    来源:AG安全团队博客
    文章版权归作者所有,未经允许请勿转载。

    THE END
    分享
    二维码
    < <上一篇
    下一篇>>