一点设想:以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今后也越来越好。
喜欢我的作品吗?别忘了给予支持与赞赏,让我知道在创作的路上有你陪伴,一起延续这份热忱!