最近 12306 换了套新验证码,又一次引发吐槽。
虽然图片组合的方式仍有不少争议,但无论如何能尝试突破传统,都是值得称赞的。
毕竟传统的验证码已饱受诟病了。为了对抗少数恶意用户,却降低了绝大部分正常人的用户体验。
每当无聊时,就会遐想各种验证码的方案,今天分享一个比较有趣的。
曾有传言,在 ATM 机上密码倒着输会自动报警。且不论真实性,就这想法还是有点意思的。
首先,也是最重要的,输入了这个反密码仍能取钱,这满足了劫匪的目的。但同时已悄悄的报警,想必也逃不远了。
其次,这无需引入一个额外的报警密码。多一个就会提升复杂度,增加记错的可能。
最后,即使平时输错,那么正好输成这个报警码的概率也是很低的。(12321 这种回文暂不考虑)
于是密码除了可以用于验证外,还可以在真与假之间建立一个规则,用于隐写额外的信息。
这个特征,是否也可用于验证码呢。
我们得设计一个圈套,让正常用户几乎不会落入,而假人一不小心就会掉进这个陷阱里。
通常情况下,破解验证码的程序都是直接抓去图片中的像素点,然后进行分析。而用户看到的,则是屏幕上最终显示出来的内容。
在绝大多数情况下,这两者都是相同的。但我们也可以让它变得不同,人为的制造一点麻烦出来。
例如,一个内容为 d6xm 的验证码:
这是个再普通不过的验证码,破解几乎毫无压力。不过,这次玩的不再是算法上的对抗了。
我们在页面中某个隐蔽的地方,偷偷插入一个 CSS 规则:
.captcha { transform: rotate(180deg); }
将图片进行 180° 的选择。这时,呈现在用户眼前的就是这样一个东西了:
内容正好变成了 wx9p!
到底哪个是正确答案呢,就不用说了吧。既然我们考察的是用户,那当然就是眼见为实了!
但通过抓图识别的假人看来,仍是原始的像素点,并不知道页面中还有这么个小把戏,于是一不小心就中招了。
不过用户偶尔也会有手误,但如果能把 wx9p 拼错成 d6xm,那也太巧合了。想必还是开挂的可能性比较大吧。
类似输入报警密码仍能取钱,我们也尽量保持这个陷阱足够低调。即使用户提交了 d6xm 也算通过,但后台会悄悄的记录下这一切。
当某个用户达到一定可疑程度时,就可以对其采取相应的措施了。将拦截处理进行延时,以避免过早的进入对抗。
当然,上面只是举了个简单的例子。像旋转这样的规则,有着太多的局限。并非所有字符旋转后都有对应的,也不能出现回文字符串。
事实上,还可以尝试更为通用的方法。
前面提到 ATM 取款密码时,若能单独设置一个报警密码,其实也是很好的,只是要多记一条。但在验证码这里,用户是完全透明的,再复杂也是浏览器自动搞定的。
我们尝试在图片里写入两套字符串。一个非常明显,另个则像水印一样暗淡。
正常情况下,后者不仔细观察很难发现。但通过页面中的滤镜,会使两者正好相反:
于是在最终用户眼里,看见的反而是水印的版本。
除了这些,其实有无数多的变化方案。例如事先对图片进行扭曲,渲染时再还原回来,等等。
当然不论怎样,其本质都是一样的。相比传统的单一图像输出,如今在输出时,还会多一个变幻参数。必须将两者进行结合,才能得到最终的图像。
尽管这能刷掉一批傻瓜式的破解程序,但稍微高级的外挂都会升级,抓取这个变幻参数进行计算。于是进入对抗阶段。
对抗是没有标准答案的。如果要有,那就是成本最低的,才能坚持的更久。
传统的验证码出现十多年,已经积累了大量的破解经验,现成的工具和算法随处可得,图像识别进入低成本规模化。在这样形势下,还停留在纯粹的图像上,也难以大幅改进了。
但新增了一个渲染时的变量,情况就变得好转。我们可以使用前端加密混淆技术、以及各种黑魔法,进行对抗。
也许某个外挂在识图领域非常强,但对于页面脚本的了解并不深入。于是,我们就可使用另一个自己熟悉的领域,和传统进行对抗了。
当然,最完美的验证码,就是没有验证码,让页面脚本来进行自我检验。可以参考: 《对抗假人 —— 前后端结合的 WAF》 。在过去前端技术尚不发达的年代,这套系统无需验证码也能抵挡绝大多数的恶意提交和 cc 攻击。 在如今,比的就是更加密和逆向了。
无论怎样,总是可以有更低成本的综合方案,来解决实际问题,而非吊死在一棵树上。