来源 | http://www.fly63.com/article/detial/10479

什么是浏览器指纹

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

人手上的指纹之所以具有唯一性,是因为每个指纹具有独特的纹路、这个纹路由凹凸的皮肤所形成。

每个人指纹纹路的差异造就了其独一无二的特征。

那么浏览器指纹也是同理,获取浏览器具有辨识度的信息,进行一些计算得出一个值,那么这个值就是浏览器指纹。

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

对于网站而言,拿到浏览器指纹并没有实际价值,真正有价值的是这个浏览器指纹对应的用户信息。

作为网站站长,收集用户浏览器指纹并记录用户的操作,是一个有价值的行为,特别是针对没有用户身份的场景。

例如在一个内容分发网站上,用户A喜欢浏览二次元的内容,通过浏览器指纹记录这个兴趣,那么下次用户不需要登录即可向A用户推送二次元的信息。

在个人PC如此普及的当下,这也是一种内容分发的方式。

对于用户而言,建立个人上网行为与浏览器指纹之间的联系或多或少都有侵犯用户隐私的意味,特别是将你的浏览器指纹和真实的用户信息相关联起来的时候。

所幸的是这种方式对于用户的隐私侵犯比较有限、滥用用户行为也会透支用户对网站的好感。

浏览器指纹背景

浏览器指纹追踪技术到目前已经进入2.5代。

第一代是状态化的,主要集中在用户的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。

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

高级指纹

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

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

如何完整修改浏览器指纹?

接下来教大家如何修改浏览器指纹,例如修改navigator全部参数:

(function() {    'use strict';function fakeActiveVRDisplays() { return "Not Spoofed"; }    function fakeAppCodeName() {      return "Mozilla";    }    function fakeAppName() {      return "Netscape";    }function fakeAppVersion() {        return "5.0 (Windows)";    }    function fakeBattery() { return "Not Spoofed"; }    function fakeConnection() { return "Not Spoofed"; }    function fakeGeoLocation() { return "Not Spoofed"; }    function fakeHardwareConcurrency() {      return 1;    }    function fakeJavaEnabled() {      return false;    }    function fakeLanguage() {        // NOTE: TOR Browser uses American English        return "en-US";    }    function fakeLanguages() {        // NOTE: TOR Browser uses American English        return "en-US,en";    }    function fakeMimeTypes() { return "Not Spoofed"; }    function fakeOnLine() {      return true;    }    function fakeOscpu() {      return "Windows NT 6.1";    }    function fakePermissions() { return "Not Spoofed"; }    function fakePlatform() {      return "Win32";    }    function fakePlugins() {        return window.navigator.plugins;    }    function fakeProduct() {      return "Gecko";    }    function fakeServiceWorker() { return "Not Spoofed"; }    function fakeStorage() { return "Not Spoofed"; }    function fakeUserAgent() {      // NOTE: Current TOR User Agent as of 19 July 2017        // NOTE: This will need constant updating.        // NOTE: As TOR changes firefox versions each update,        // NOTE: Shape Shifter will need to keep up.        return "Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0";    }    function fakeBuildID() {      return "20100101";    }
const fakeActiveVRDisplaysValue       = fakeActiveVRDisplays();    const fakeAppCodeNameValue            = fakeAppCodeName();    const fakeAppNameValue                = fakeAppName();    const fakeAppVersionValue             = fakeAppVersion();    const fakeBatteryValue                = fakeBattery();    const fakeConnectionValue             = fakeConnection();    const fakeGeoLocationValue            = fakeGeoLocation();    const fakeHardwareConcurrencyValue    = fakeHardwareConcurrency();    const fakeJavaEnabledValue            = fakeJavaEnabled();    const fakeLanguageValue               = fakeLanguage();    const fakeLanguagesValue              = fakeLanguages();    const fakeMimeTypesValue              = fakeMimeTypes();    const fakeOnLineValue                 = fakeOnLine();    const fakeOscpuValue                  = fakeOscpu();    const fakePermissionsValue            = fakePermissions();    const fakePlatformValue               = fakePlatform();    const fakePluginsValue                = fakePlugins();    const fakeProductValue                = fakeProduct();    const fakeServiceWorkerValue          = fakeServiceWorker();    const fakeStorageValue                = fakeStorage();    const fakeUserAgentValue              = fakeUserAgent();    const fakeBuildIDValue                = fakeBuildID();Object.defineProperties(window.navigator, {        /*        activeVRDisplays: {            configurable: true,            enumerable: true,            get: function getActiveVRDisplays() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.activeVRDisplays");                return fakeActiveVRDisplaysValue;            }        },        */appCodeName: {            configurable: true,            enumerable: true,            get: function getAppCodeName() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.appCodeName");return fakeAppCodeNameValue;            }        },        appName: {            configurable: true,            enumerable: true,            get: function getAppName() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.appName");return fakeAppNameValue;            }        },        appVersion: {            configurable: true,            enumerable: true,            get: function getAppVersion() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.appVersion");return fakeAppVersionValue;            }        },// TODO: This is getBattery() now        /*        battery: {            configurable: true,            enumerable: true,            get: function getBattery() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.battery");                return fakeBatteryValue;            }        },        connection: {            configurable: true,            enumerable: true,            get: function getConnection() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.connection");                return fakeConnectionValue;            }        },        geolocation: {            configurable: true,            enumerable: true,            get: function getGeoLocation() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.geolocation");                return fakeGeoLocationValue;            }        },        */hardwareConcurrency: {            configurable: true,            enumerable: true,            get: function getHardwareConcurrency() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.hardwareConcurrency");return fakeHardwareConcurrencyValue;            }        },/*        javaEnabled: {            configurable: true,            enumerable: true,            value: function getJavaEnabled() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.javaEnabled");                return fakeJavaEnabledValue;            }        },        */language: {            configurable: true,            enumerable: true,            get: function getLanguage() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.language");return fakeLanguageValue;            }        },        languages: {            configurable: true,            enumerable: true,            get: function getLanguages() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.languages");return fakeLanguagesValue;            }        },/*        mimeTypes: {            configurable: true,            enumerable: true,            get: function getMimeTypes() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.mimeTypes");                return fakeMimeTypesValue;            }        },        */onLine: {            configurable: true,            enumerable: true,            get: function getOnLine() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.onLine");return fakeOnLineValue;            }        },        oscpu: {            configurable: true,            enumerable: true,            get: function getOscpu() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.oscpu");return fakeOscpuValue;            }        },/*        permissions: {            configurable: true,            enumerable: true,            get: function getPermissions() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.permissions");                return fakePermissionsValue;            }        },        */platform: {            configurable: true,            enumerable: true,            get: function getPlatform() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.platform");return fakePlatformValue;            }        },/*        plugins: {            configurable: true,            enumerable: true,            get: function getPlugins() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.plugins");                return fakePluginsValue;            }        },        */product: {            configurable: true,            enumerable: true,            get: function getProduct() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.product");return fakeProductValue;            }        },/*        serviceWorker: {            configurable: true,            enumerable: true,            get: function getServiceWorker() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.serviceWorker");                return fakeServiceWorkerValue;            }        },        storage: {            configurable: true,            enumerable: true,            get: function getStorage() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.storage");                return fakeStorageValue;            }        },        */userAgent: {            configurable: true,            enumerable: true,            get: function getUserAgent() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.userAgent");return fakeUserAgentValue;            }        },        buildID: {            configurable: true,            enumerable: true,            get: function getBuildID() {                console.log("[ALERT] " + window.location.hostname + " accessed property Navigator.buildID");return fakeBuildIDValue;            }        }    });})();

另外关于浏览器硬件指纹,包括canvas,webgl,fonts,audio等。

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.", 2, 2);        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字符串上,从而产生一个巨大的字符串,这个字符串在每台设备上可能是非常独特的。

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

// 部分代码 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 = 3vertexPosBuffer.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) */}

防御浏览器指纹追踪

这是一个比较暴力的方法,直接禁止网站使用JavaScript可以非常有效地防御浏览器指纹追踪,但是这样会导致页面较大部分地功能不可用。

而且非常不幸的是,即便禁止了js但是还可以通过css来采取浏览器的信息,例如:

@media(device-width: 1080px) {  body {    background: url("https://example.org/1080.png");  }}

总结

对于浏览器指纹,攻与防在不断的转换,目前浏览器指纹也不能绝对的标识一台主机,如果用户切换显卡或者双系统,虚拟机这些因素,那么目前的浏览器指纹就无法唯一标识了。

未来随着新的HTML5技术不断更新,新的浏览器技术会提供更多的API,以及通过侧信道技术,在浏览器指纹会有新的突破。

学习更多技能

请点击下方公众号

浏览器指纹追踪技术,如何完整修改浏览器指纹?相关推荐

  1. 什么是浏览器指纹追踪(浏览器指纹追踪修改,浏览器指纹追踪技术)

    浏览器指纹追踪 现实世界可通过体貌特征.身份证件.生物特征(如指纹信息)等手段对用户进行唯一性识别.Web 世界主要通过 Cookies 技术.浏览器指纹.WebRTC 等技术进行识别. 典型追踪技术 ...

  2. 反浏览器指纹追踪(反浏览器指纹追踪技术)

    浏览器指纹追踪是一种在网络上追踪用户信息的方法.而在大数据时代,更多的人不希望自己的信息被过多收集.为了反制浏览器指纹追踪行为,反浏览器追踪技术也在不断发展. 什么是反浏览器指纹追踪技术? 浏览器指纹 ...

  3. 2.5代指纹追踪技术—跨浏览器指纹识别

    01. 研究背景 在如今,做安全防御已经不仅仅是被动的等着攻击者攻击,作为防御方,有越来越多的方法去反击攻击者,甚至给攻击者一些威胁. 设备指纹技术是一种长久有效的追踪技术,即使攻击者挂再多 vpn, ...

  4. 指纹浏览器指纹追踪技术:指纹浏览器开源代码,浏览器指纹js插件

    指纹追踪技术(指纹浏览器)的前世今生: 1).第一代 第一代指纹追踪是cookie这类的服务端在客户端设置标志的追踪技术,evercookie 是 cookie 的加强版. . 第二代 第二代指纹追踪 ...

  5. Canvas 指纹追踪技术

    目录 1. 设备指纹技术介绍 1.1 第一代 -- cookie / evercookie 1.2 第二代 -- 浏览器指纹技术 1.3 第三代 -- 发现设备后面的人 2. 指纹的分类 2.1 普通 ...

  6. 浏览器指纹追踪技术有哪些

    当我们在进行浏览器网站时,都是会留下痕迹的,哪怕是打开了隐私浏览,运营商也是可以看到相关的浏览数据信息的,那浏览器指纹是如何被追踪到的呢? Cookie Cookie是目前浏览器最常用的一种方式,当我 ...

  7. IE浏览器停止服务,曾经的王者为何退役?解析浏览器背后的技术是什么?

    前言 随着Windows的官宣,ie浏览器将于6月16日正式退役. 那个曾几何时的浏览器之王,经历一次又一次操作系统的迭代,直至新兴浏览器的出现后沦为浏览器鄙视链的最后一层,在叹息与嘲笑声中跌出神坛, ...

  8. 跨境电商账号关联指南:电商浏览器下载与使用,电商浏览器防关联

    在跨境电商运营中,账号多开可谓必不可少.但是随之而来的就是账号关联的风险,每年都有无数商家因为帐号关联而被封号.封铺.为了解决大家的烦恼,今天我带来一份账号关联指南,介绍一下在防关联问题上,电商浏览器 ...

  9. 初探Web客户端追踪技术

    初探Web客户端追踪技术 http://zoo.zhengcaiyun.cn/blog/article/webclient 前言 案例1 当我们首次浏览网站时,在网页的下方位置经常会出现提示,询问是否 ...

最新文章

  1. [原创]Fluent NHibernate之旅
  2. cta策略 有哪些_【投资理财】基本面拐点已至,当前环境适合配置哪些策略?...
  3. LAMP+LNMP(四)PHP安装实践
  4. nfs服务器_Kubernetes集群下部署NFS持久存储
  5. DBShop电子商务系统
  6. 11g R2 RAC客户端负载均衡配置
  7. 洛谷 P1509 找啊找啊找GF(复习二维费用背包)
  8. Visual C# 2008+SQL Server 2005 数据库与网络开发-- 5.1 计算
  9. 汇编语言--x86汇编指令集大全
  10. 用python统计字母个数_如何用python统计字符串中字母个数?
  11. 发现了一个开源的sip软电话项目(C#)
  12. 恶意点击软件测试简历,亲测百度竞价的恶意点击:60%广告费浪费
  13. Rinne Loves Sequence
  14. Apple iPhone
  15. 在PS中如何旋转箭头
  16. proxmark3模拟amiibo速通
  17. 用js实现背景颜色改变
  18. mysql查询余额变化,SQL查询解决方案-逐日余额
  19. Django(10)-模板层的变量和标签
  20. 企业中一些职位的简称

热门文章

  1. 什么是开源协议?知识产权又是什么?—— 版权与开源简介
  2. CART-回归树 实例
  3. Python爬虫案例-猪八戒
  4. WPF的依赖属性和附加属性(用法解释较全)
  5. html手机模块化,jQuery 移动端拖拽(模块化开发,触摸事件,webpack)
  6. VS通过自定义宏设置环境变量
  7. vue+element项目 手机号、邮箱校验 保姆级教程
  8. 求教Word自动生成的目录中标题无法顶格,编号与标题之间也存在空格无法消除怎么办,
  9. 手撸一套RBAC权限系统
  10. 【个人开发】通过企业微信实现消息推送到个人微信