一点设想:以canvas指纹应对滥用马甲问题
本文是给站方@Matty 的一些技术参考,或许能减轻matters个别用户反复注册新号来扰乱秩序的问题。
文章的灵感来自这里,有个用户注册了很多小号在评论区骂人:
这位说得对,只封ip是不够的。正好我最近比较闲,所以写篇文章向大家介绍一种即使用了VPN隐藏ip,甚至切到小号、开启隐私模式,依然能对用户进行唯一标识的技术。canvas指纹是“浏览器指纹”的一种,也就是说它只能确定“对方是不是来自同一电脑上的同一浏览器”,仅凭这种技术,无法确定“对方的现实身份是什么”,应当与matters的理念不冲突。
目前canvas指纹已经得到了广泛应用,据我所知,淘宝就用了canvas指纹识别用户,所以技术上完全可行。
canvas指纹是什么?
canvas的直译是“画布”,是一个可以使用脚本(通常为JavaScript)来绘制图形的 HTML 元素。通过调用js脚本,可以让浏览器自己在网页中绘制出图形。详细的使用方法可见:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial。不过作为本文的读者不必了解那么多,只知道canvas能够在网页上绘制图形即可。
canvas和普通网页图片的区别在哪里?可以这样理解:普通的网页图片放在服务器上,当用户需要查看图片的时候,服务器就把图片发给客户端。如果大家使用不同的电脑访问同一张图片,得到的图片是相同的。而canvas图片只是一系列绘图的“指令”,服务器告诉客户端“怎么画”,具体的绘画工作由客户端完成。简单概括就是:普通的网页图片是已经确定好的,而canvas是你的浏览器临时画出来的。
两个不同的人,即使种族相同、甚至父母相同,指纹也不同。电脑也是如此:两台电脑即使品牌、操作系统、浏览器等等完全相同,电脑和电脑之间也会有细微的差别。反映在canvas上,导致了即使两台电脑收到了相同的“绘图指令”,画出来的图案也不相同。但是让同一台电脑反复执行同样的绘画指令,得到的图案是完全相同的。
大家可以在:https://browserleaks.com/canvas看看自己的canvas指纹,Uniqueness一项通常可以达到99.9%以上。
示例项目:
思路:在页面上画一个用户肉眼看不到的canvas,用toDataURL获取图案的特征值,去掉开头相同的部分,得到一个特征字符串。特征字符串太长,不好肉眼比对,所以又做了一遍哈希,得到最终的canvas指纹。算出指纹后,以alert弹出。
给新手的指引:新建一个文本文档,把我的代码粘贴进去,保存后把文件扩展名从txt改为html,然后用浏览器打开,点击按钮。可以看到,开不开隐私模式,都不影响canvas指纹的值。
canvas指纹的示例代码(严格来说不能算是我写的,我只是把不同人的代码拼到一起而已,码农日常啦):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>canvas model</title> </head> <body> <script> /** * Calculate a 32 bit FNV-1a hash * Found here: https://gist.github.com/vaiorabbit/5657561 * Ref.: http://isthe.com/chongo/tech/comp/fnv/ * * @param {string} str the input value * @param {boolean} [asString=false] set to true to return the hash value as * 8-digit hex string instead of an integer * @param {integer} [seed] optionally pass the hash of the previous chunk * @returns {integer | string} */ function hashFnv32a(str, asString, seed) { /*jshint bitwise:false */ var i, l, hval = (seed === undefined) ? 0x811c9dc5 : seed; for (i = 0, l = str.length; i < l; i++) { hval ^= str.charCodeAt(i); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } if (asString) { // Convert to 8 digit hex string return ("0000000" + (hval >>> 0).toString(16)).substr(-8); } return hval >>> 0; } </script> <script> function getCanvasFingerprint() { var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); // Ref.: https://browserleaks.com/canvas // Text with lowercase/uppercase/punctuation symbols var txt = "BrowserLeaks,com <canvas> 1.0"; ctx.textBaseline = "top"; // The most common type ctx.font = "14px 'Arial'"; ctx.textBaseline = "alphabetic"; ctx.fillStyle = "#f60"; ctx.fillRect(125, 1, 62, 20); // Some tricks for color mixing to increase the difference in rendering ctx.fillStyle = "#069"; ctx.fillText(txt, 2, 15); ctx.fillStyle = "rgba(102, 204, 0, 0.7)"; ctx.fillText(txt, 4, 17); var b64 = canvas.toDataURL("image/png").replace("data:image/png;base64,", ""); var crc = hashFnv32a(b64, true, undefined); return crc; } function popFingerprint() { alert(getCanvasFingerprint()); } </script> <button onclick="popFingerprint()">canvas fingerprint</button> </body> </html>
最后说说一些和题目无关的建议:
- 不少用户因为评论而被封禁,但是评论不会被删除。为评论区的气氛考虑,希望推出“自动折叠被封禁用户的评论”的功能。
- 当前的条件下,可以让用户获得发言权限(发文章和评论)之前手写一段“为什么要加入matters”的申请,等待人工审核通过后方可发言。这一方法是小论坛常用的方式,能够有效过滤恶意注册。以matters当前的活跃人数,既然诉讼都能人工计票,想要发言的人数应该也在人工审核的能力之内。
- 如果以后流量增加了,垃圾广告账号、批量注册机器人也会随之而来。不少服务商提供防恶意注册服务,站方可以现在就留意一下。
作为一个潜水用户,看到近半年来,社区规则确实发展得逐渐完善。matters是为数不多的氛围友善的论坛,这是我喜欢它的原因。祝matters今后也越来越好。