首页 文章详情

京东架构师:什么是浏览器指纹?我:泄漏隐私的东西

前端阳光 | 231 2022-03-17 11:59 0 0 0
UniSMS (合一短信)

点击上方 前端阳光,关注公众号

回复加群,加入技术交流群交流群


什么是浏览器指纹

浏览器指纹可以通过浏览器对网站可见的配置、设置信息,来跟踪 Web 浏览器,它就像我们人手上的指纹一样,具有个体辨识度,只不过现阶段浏览器指纹辨别的是浏览器。

浏览器指纹辨识的信息可以是 UA、时区、地理位置或者是使用的语言等等,浏览器所开发的信息决定了浏览器指纹的准确性。

对于网站而言,拿到浏览器指纹并没有实际价值,真正有价值的是浏览器指纹对应的用户信息。作为网站站长,收集用户浏览器指纹并记录用户的操作,是一个有价值的行为,特别是针对没有用户身份的场景。

例如一个视频网站,未注册该网站的用户 A 喜欢浏览二次元的视频,通过浏览器指纹记录这个,那么下次可以直接向该浏览器推送二次元的视频。因为现在的上网设备大都是私人的,这样的推送方式很容易获得大部分用户的好感,从而使之成为网站的用户。

浏览器指纹的发展

浏览器指纹技术的发展跟大多数技术一样,并非一蹴而就的,现有的几代浏览器指纹技术是这样的:

  • 第一代是状态化的,主要集中在用户的 cookie 和 evercookie 上,需要用户登录才可以得到有效的信息。
  • 第二代才有了浏览器指纹的概念,通过不断增加浏览器的特征值从而让用户更具有区分度,例如 UA、浏览器插件信息等
  • 第三代是已经将目光放在人身上了,通过收集用户的行为、习惯来为用户建立特征值甚至模型,可以实现真正的追踪技术。但是目前实现比较复杂,依然在探索中。

目前浏览器指纹的追踪技术可以算是进入 2.5 代,这么说是因为跨浏览器识别指纹的问题仍没有解决。

指纹采集

信息熵(entropy)是接收的每条消息中包含的信息的平均量,信息熵越高,则能传输越多的信息,信息熵越低,则意味着传输的信息越少。

浏览器指纹是由许多浏览器的特征信息综合起来的,其中特征值的信息熵也不尽相同。因此,指纹也分为基本指纹和高级指纹。

  • 「基本指纹」

基本指纹就是容易被发现和修改的部分,如 http 的 header。

{  "headers": {    
    "Accept""text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",     
    "Accept-Encoding""gzip, deflate, br",     
    "Accept-Language""zh-CN,zh;q=0.9,en;q=0.8",     
    "Host""httpbin.org",     
    "Sec-Fetch-Mode""navigate",     
    "Sec-Fetch-Site""none",     
    "Sec-Fetch-User""?1",     
    "Upgrade-Insecure-Requests""1",     
    "User-Agent""Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"
  }}

除了 http 中拿到的指纹,还可以通过其他方式来获得浏览器的特征信息,例如:

  • 每个浏览器的UA
  • 浏览器发送的 HTTP ACCEPT 标头
  • 浏览器中安装的浏览器扩展/插件,例如 Quicktime,Flash,Java 或 Acrobat,以及这些插件的版本
  • 计算机上安装的字体。
  • 浏览器是否执行 JavaScript 脚本
  • 浏览器是否能种下各种 cookie 和 “super cookies”
  • 是否浏览器设置为“Do Not Track”
  • 系统平台(例如 Win32、Linux x86)
  • 系统语言(例如 cn、en-US)
  • 浏览器是否支持触摸屏

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

这些信息就类似人类的体重、身高、肤色一样,有很大的重复概率,只能作为辅助识别,所以我们需要更精确的指纹来判断唯一性。

  • 「高级指纹」

普通指纹是不够区分独特的个人,这时就需要高级指纹,将范围进一步缩小,甚至生成一个独一无二的跨浏览器身份。

用于生产指纹的各个信息,有权重大小之分,信息熵大的将拥有较大的权重。

  • 「Canvas 指纹」

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

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

function getCanvasFingerprint ({    
    var canvas = document.createElement('canvas');    
    var context = canvas.getContext("2d");    
    context.font = "18pt Arial";    
    context.textBaseline = "top";    
    context.fillText("Hello, user."22);    
    return canvas.toDataURL("image/jpeg");
}
getCanvasFingerprint()

流程很简单,渲染文字,toDataURL 是将整个 Canvas 的内容导出,得到值。

  • 「WebGL 指纹」

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

WebGL 报告——完整的 WebGL 浏览器报告表是可获取、可被检测的。在一些情况下,它会被转换成为哈希值以便更快地进行分析。

WebGL 图像 ——渲染和转换为哈希值的隐藏 3D 图像。由于最终结果取决于进行计算的硬件设备,因此此方法会为设备及其驱动程序的不同组合生成唯一值。这种方式为不同的设备组合和驱动程序生成了唯一值。

可以通过 Browserleaks test 检测网站来查看网站可以通过该 API 获取哪些信息。

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

怎么获取浏览器指纹?

最主要的需求就是判断本次请求的用户到底是新用户还是老用户还是机器人。通过request去分析ip、ua、mac地址都有各种缺点导致不准确。

Fingerprintjs2 是通过纯前端原生js实现的浏览器指纹采集器,通过获取浏览器中所有能获取到的信息(部分通过base64转成String),最后生成出md5,用于该用户在该设备上的唯一标识码,官方宣称准确度高达99.5%

「测试代码」

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Fingerprint2 TEST</title>
    <style>
        body {
            color#555;
        }
        #info {
            font-size12px;
        }
        #control span {
            color#333;
            margin-left10px;
        }
    
</style>
</head>
<body>
<div id="info">
    <p>Fingerprint2 Github: <a href="https://github.com/Valve/fingerprintjs2" target="_blank">https://github.com/Valve/fingerprintjs2</a>
    </p>
    <p>纯前端实现的浏览器指纹采集器,通过获取浏览器中所有能获取到的信息(部分通过base64转成String),最后生成出md5,用于该用户在该设备上的唯一标识码,官方宣称准确度高达99.5%</p>
</div>
<div id="control">
    <button onclick="start()">开始</button>
    <span>userAgent:</span><input type="checkbox" id="userAgent" checked="checked">
    <span>fonts:</span><input type="checkbox" id="fonts" checked="checked">
    <span>fontsFlash:</span><input type="checkbox" id="fontsFlash" checked="checked">
    <span>canvas:</span><input type="checkbox" id="canvas" checked="checked">
    <span>webgl:</span><input type="checkbox" id="webgl" checked="checked">
    <span>audio:</span><input type="checkbox" id="audio" checked="checked">
    <span>enumerateDevices:</span><input type="checkbox" id="enumerateDevices" checked="checked">
</div>
<div id="view">
</div>
<script src="https://cdn.staticfile.org/fingerprintjs2/2.1.0/fingerprint2.min.js"></script>
<script>
    function start({
        const start = new Date().getTime();
        let view = document.querySelector('#view');
        view.innerHTML = '';
        let excludes = {};
        if (!document.querySelector('#userAgent').checked) {
            excludes.userAgent = true;
        }
        if (!document.querySelector('#audio').checked) {
            excludes.audio = true;
        }
        if (!document.querySelector('#enumerateDevices').checked) {
            excludes.enumerateDevices = true;
        }
        if (!document.querySelector('#fonts').checked) {
            excludes.fonts = true;
        }
        if (!document.querySelector('#fontsFlash').checked) {
            excludes.fontsFlash = true;
        }
        if (!document.querySelector('#webgl').checked) {
            excludes.webgl = true;
        }
        if (!document.querySelector('#canvas').checked) {
            excludes.canvas = true;
        }
        let options = {excludes: excludes}

        Fingerprint2.get(options, function (components{
            // 参数
            const values = components.map(function (component{
                return component.value
            });
            // 指纹
            const murmur = Fingerprint2.x64hash128(values.join(''), 31);
            view.innerHTML += '<p>指纹 : ' + murmur + '</p>';
            view.innerHTML += '<p>消耗 : ' + (new Date().getTime() - start) + ' 毫秒</p>';
            view.innerHTML += '<p>使用的参数 : </p>';
            for (const c of components) {
                view.innerHTML += '<p>' + c.key + ' : ' + c.value + '</p>';
            }
        });
    }
</script>
</body>
</html>

「效果演示」

测试地址:https://tczmh.gitee.io/fingerprint2demo/

最终效果:

指纹 : 959c35da0972771684b62c210fee5438
消耗 : 351 毫秒
使用的参数 :
userAgent : Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
webdriver : not available
language : zh-CN
colorDepth : 24
deviceMemory : 4
pixelRatio : 1
hardwareConcurrency : 4
screenResolution : 900,1600
availableScreenResolution : 900,1600
timezoneOffset : -480
timezone : Asia/Shanghai
sessionStorage : true
localStorage : true
indexedDb : true
addBehavior : false
openDatabase : true
cpuClass : not available
platform : Linux x86_64
doNotTrack : 1
plugins : Chrome PDF Plugin,Portable Document Format,application/x-google-chrome-pdf,pdf,Chrome PDF Viewer,,application/pdf,pdf,Native Client,,application/x-nacl,,application/x-pnacl
(后面太长省略)…

作者:涅槃快乐是金 链接:https://www.jianshu.com/p/df5be4e46d18


技术交流群


我组建了技术交流群,里面有很多 大佬,欢迎进来交流、学习、共建。回复 加群 即可。后台回复「电子书」即可免费获取 27本 精选的前端电子书!



   “分享、点赞在看” 支持一波👍

good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter