曾经某大师给我展示了这么一个使用场景。
某片子网站有一套免登陆识别客户端用户的机制,用户不需要注册、登录,直接访问链接即可在网站上实现一些会员服务。按正常思维,需要实现会员制,必须要求用户在网站上登录留下凭证,这样服务器才能把对应会员正确的服务分发,黄金会员白金会员钻石会员,各种会员,当然,如果用户充值,这些帐会自动记录在当前客户端名下。看着似乎有点玄乎,当时就被唬住了。
按照常规的思路,想要获取一个能当做用户唯一标识的东西,从浏览器端来看,要么MAC地址之类的?类似于这些相对固定而唯一的号,然鹅,浏览器除了提供UA(User agent)之外,其它信息一概无法获取,而UA根本就无法用来识别浏览器端的唯一性,因为相同设备相同浏览器的UA是一致的,美梦似乎要破灭了。然而我那锲而不舍的精神一直支撑着我,让我不断向前~探索。
小片子网站能做,应该还是有对应的技术可以实现的,后面回来后翻了下资料,发现还真有这种操作,不用注册不用登陆就可以区分浏览器端用户!感觉这个需求瞬间就靠谱了,PM不能随便打,还好这么冷静。翻开了我的小本子,在里面狠狠地记了一笔。
这么看来是要能造一个类UUID(Universally Unique IDentifier)的东西出来标识一下用户才行,具体怎么造,这里就不详细说了,GitHub上也有很多基友写好的码子可以下,有时间了解原理的小伙伴也可以科研一下。至于造UUID这种事情,肯定是要交给我们服务端来干了。那么问题来了,服务端辛苦造出来的UUID该怎么分发给浏览器端并在客户端本地存起来用来标识唯一用户呢?服务器通过页面?cookie?URL?各种本地存储?似乎都不行,清理一下浏览器、关下机又是空白一片,充值的会员还没来得及看片就没掉了,这是不允许的。
现有的浏览器没有现成接口标识当前浏览器用户的唯一性,认真搜刮了一番,似乎有好几种聊的比较多的方法,这里就选个看上去相对靠谱一点的方法聊一聊:
HTML5 Canvas指纹、AudioContext指纹
先说说第一种Canvas指纹方法,基于Canvas标签绘制特定内容的图片,使用canvas.toDataURL()方法获得图片内容的base64编码(对于PNG格式的图片,以块(chunk)划分,最后一块是32位CRC校验)作为唯一性标识,JavaScript示例:
function getUUID() {
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#FF0000';
ctx.fillRect(0,0,8,10);
var b64 = canvas.toDataURL().replace('data:image/png;base64,', '');
var bin = window.atob(b64);
var crc = bin2hex(bin.slice(-16,-12));
return src;
}
alert(getUUID());
这种方法是基于相同的HTML5 Canvas元素的绘制操作在不同操作系统、不同浏览器上,产生图片内容不完全相同原理实现的。在图片处理上,不同浏览器使用了不同的图形处理引擎、不同的默认压缩级别等。从像素级别来看,操作系统各自使用了不同的设置和算法来进行像素渲染。即使相同的绘图操作,不同的浏览器端产生的图片数据的CRC检验也不相同。这个特性可以用来区分大部分的浏览器端,但仍然没办法做到完全区分。碰撞的可能性比较大。
而从Canvas被浏览器兼容的情况来看,几乎已被所有主流浏览器都支持Canvas,覆盖了大部分的PC、平板、智能手机端用户。早期有不少人选择这种方式实现浏览器端唯一性标识。
另一种是AudioContext指纹,原理与canvas类似。本地计算机或浏览器硬件或软件的细微差别,导致音频信号的处理上的差异,相同器上的同款浏览器产生相同的音频输出,不同机器或不同浏览器产生的音频输出会存在差异。
AudioContext指纹有两种方式实现:
方法一: 生成音频信息流(三角波),对其进行FFT变换,计算SHA值作为指纹,音频输出到音频设备之前进行清除,用户毫无察觉。
第二种方法是:生成音频信息流(正弦波),进行动态压缩处理,计算MD5值。
这种方法出来的指纹仍然不是可靠的,与Canvas指纹类似,有很高的碰撞概率。似乎离实际运用还有一段距离。
为了提高这两种指纹的可靠性,我们需要额外附加一些其它信息,降低碰撞概率。例如增加一些硬件指纹作为补充,有兴趣的小伙伴可以详细阅读一下这个文档: Hardware fingerprint using HTML5。主要的硬件模块包括:GPU’s clock frequency、Camera、Speakers/Microphone、Motion sensors、GPS、Battery等等。
即便如此,虽然碰撞的概率降低了,但仍然无法达到一个比较理想的状态。那需要怎样再做优化呢?没错,将上述所有的指纹综合利用,进行分析、计算哈希值作为综合指纹,可以大大降低碰撞率,极大提高客户端唯一性识别的准确性。
似乎事情得到解决了,然而细想会发现,这种唯一性验证在跨浏览器的时候就无法使用了,同一个用户在同一台机器上使用不同的浏览器所产生的指纹是不一样的。在Chrome充值的会员在Firefox上不能用了,好像也不太科学。又是一顿好找,还真有大佬在研究这块跨浏览器指纹,感兴趣的童鞋可以研读膜拜一下这篇: ( cross-) Browser fingerprint via OS and Hardware level features
其原理是根据浏览器、操作系统与底层硬件之间的交互进而分析出来的指纹,同一机器上不同浏览器产生的这种指纹是一致的。
总结一下
如果你上面的内容似乎看不太懂,可以直接跳到这里来。
客户端用户唯一性识别可靠性不够高,在利用综合指纹识别时虽然碰撞概率降低,但仍然需要解决一些例如跨浏览器身份识别这种问题,需要深挖。运用场景比较广,可用于免登录、客户端追踪(例如根据用户搜索内容,广告精确定推等等)一些精度要求不太高的地方。对于免登录场景运用,可结合后期用户的一些信息做进一步区分处理,但仍然达不到注册的身份验证方法的精度。
对于客户端用户唯一性识别你怎么看呢?