从验证码第一次被应用于网络中,早期的验证码是一串非常简单的形状标准的数字,经过长期发展形式越来越多样化,现在简单的数字英文验证码已经很容易被机器读取破解,复杂的验证码设计得愈发反人类。并且绝大多数的网站验证码应用在用户体验上都存在一个通病,那就是用户往往输入了用户名和密码等信息点击“登陆”或者“提交”按钮后却莫名其妙地刷新一遍页面提示让你输入验证码——为什么第一遍前端不提示,等用户提交页面后又弹个提示让用户输入验证码?这往往让用户有种被愚弄的感觉。这一点,搜索引擎公司百度做得尤为突出,堪称处处有验证码,处处让你抓狂。以上种种,都暴露了验证码的弊端,那么有人去思考验证码有必要存在吗?复杂的验证码虽然起到了防范非人为操作的作用,但是带来了大量时间及效率上的损失。我们从效率上来考量, 有必要认真分析一下,验证码是否是必须的?
验证码在理论上是否有存在的必要性呢?
人为操作与机器操作(非人为操作)的区别也许有点隐晦。我们可以设想一个情景,在未来,一栋高科技大楼中你需要通过一道电子门,而这个门有一个识别程序,当判定通过者为人类才可以打开,如果是机器人那么就不开门。而你作为一个活生生的人类站在这道门前,却需要回答一个可笑的验证问题:"请问8+2=?" ,当你准备输入了10的时候,却按错了一个按键,门锁住了。这时候来了一个机器人,它同样准确无误地输入了10,这时候门打开了。后果我想大家可想而知,机器人大军冲破了人类最后的防线,直取人类核心机密......而作为人类抵抗军的最后幸存者,却被无情得挡在了门外.......
这是不是一个看起来一个很愚蠢的电子门?我想大部分人在此都会自然而然的发出质疑:难道区分人类和机器就一定要用这种看似可笑的问题吗?为什么不从人类与机器的本质不同上加以区别呢,比如人类的生命体征、人体的热辐射等等...
其实现在的验证码就在做和这道电子门同样的事——在铁路订票的网站上煞费苦心弄出来一套奇形怪状的验证码,人眼都难以识别,机器是否不能识别还存疑(有数据显示,90%以上的验证码对注册机、刷票程序等没有用处,反而对人绝对有用)。机器与人为操作的结果都是对服务器发送请求,而在发送网络请求的时候人为操作的痕迹已经完全没有了,换句话说,当我们点击"提交"按钮数据已经发送到网络上以后是无法验证这个请求操作是来自人还是机器。那么我们考虑网页上当一个人类用户去输入文字的时候是怎样的一个过程:光标从别处移入到信息输入框内,光标变成一个"I形"的可输入符,点击鼠标左键,光标再次变成闪烁的输入提示符,重复(按下键盘按键,按键抬起),完成输入。这个过程是机器不会去做的,所有的自动登录的软件都是直接发送请求到服务器,机器并不去模拟用户的动作,因为没有任何意义(至少在本文发出去前是这样)。而这个人为动作我们却是我们验证操作来源或者叫"人机识别"的关键。我们在页面的脚本代码中加以验证鼠标是否在移入了指定的输入框时状态发生了改变?如果鼠标变成了输入符,则继续验证:是否在这之后点击量鼠标左键,如果有,则进入输入状态,继续验证:在输入状态下是否有按键按下和抬起?在按键按下和抬起中可以记录按下了哪一个键,然后在按键抬起的时候与真实输入框内的字符进行比较是否一致(好在现在的密码是没有中文字符的),如果一致则认为是一个人工的输入,之后在最后点击按钮的时候可以再次进行综合验证。思路是这样,当然聪明的程序员可以把这个验证过程加以完善,我相信可以做到不需要验证码一样可以完全识别出有效的人为操作。我稍后会给出一个实例。
举个例子说明一下。我们在网络上搜索"微信自动发消息",这是一个很合适的例子,借此可以论证能否不通过人为操作而实现自动发消息。看这个页面:
截至发文目前最新的微信网页版页面,我们注意一下下方的输入框(中间有字符"Jason"的一个框),通过谷歌浏览器查看它的ID是editArea, 然后再注意一下右下角的发送按钮:
查看得到它的class是 .btn .btn_send ,好的,之后我们对这两个控件进行一系列操作实验。
我们尝试编写自动发消息的客户端脚本代码:
$('#editArea').append("Jason");
$('.btn_send').trigger("click");
上面的Js代码就是模拟在输入框内追加文字"Jason",然后模拟点击"发送"按钮,我们姑且称作为"攻击脚本",大家可以在谷歌浏览器或者firefox浏览器里的控制台输入这段"攻击脚本",试验一下看看能不能自动发送消息?
好,经过我的试验,结果是没有发送出去。这是为什么呢?难道是该页面用了什么特殊方法判断出了非人为操作吗?到这里我和大家一样困惑,不要急。经过查看页面源码和脚本等信息,我发现该页面用了angular库对信息的处理上做了一些封装,导致我们以直接操作DOM元素的方式无效:页面并不是在点击发送按钮时直接取输入框内的文字的,我们直接在输入框追加文字实际上是没有任何作用的。我又去网上搜索了一下angular库的教程,了解到该页面是对用户每一次的键盘按下和抬起输入的字符进行储存,保存到一个变量中,最终实现输入框内的字符和这个变量同步,最后发送时取这个变量中的内容,而不是输入框里的内容。由此我们对之前的"攻击脚本"进行一些修改 :
var appElement = document.querySelector("#editArea");
var $scope = angular.element(appElement).scope();
$scope.$apply(function () { $scope.editAreaCtn = "Jason"; }); $('.btn_send').trigger("click");
以上修改后的代码放在浏览器控制台(F12)执行后,页面自动发送了消息"Jason",至此我们成功实现了无人操作的自动发送消息。
这个例子刚好成了一个反例,相信聪明的读者能从中得到了一些启发。该例中的主角——Angular库把用户的输入过程做了记录,但是它并没有利用这个过程作后续的“人机识别”验证,因为这个库目的本来就不是干这事的。但它也引出了本文的思考,利用识别那些机器不会做的操作来区分人机操作,核心思想不是搞什么莫名其妙的验证码(很原始的做法)来让人来费劲看那是什么字符--完全是自找麻烦,区分人还是机器,应从人和机器最本质的属性区别入手。。。