生活在互联网时代,我们会经常要跟网页打交道。即便现在手机APP也在不断的发展,Web应用依旧是最古老最常见的。在与用户进行交互的过程中,验证码有时候扮演着重要的角色,比如在用户注册,登录,提交表单的场景中,为了防止机器人浪费服务器的资源,我们需要验证在操作的是真实的用户而不是机器人。作为开发者我们当然可以从零开发一个验证码组件,不过很可能会不够完善。 Google reCAPTCHA 是一个不错的选择,一个月可以支持一百万次免费验证。
然而在搜索了Google reCAPTCHA相关的资料[1][2][3]后,发现现有的教程并不是很完善,比如:
- 没有很详尽的前后端分离验证代码(可能因为起服务器比较麻烦
- 几乎所有的demo代码语言都是PHP(其实就是想拿Python写一个😁
- 最新的v3版本的资料很少
所以我写了一个 Python版本的demo,直接在本地起服务器打开网页就可以看到所有版本的效果,并且代码简单易于调试,有兴趣的可以动手操作一下以加深对Google reCaptcha的理解。下面我会结合代码和效果来介绍各个版本的使用方法。
注册账号和配置
首先去 Google reCAPTCHA Admin Console 注册账号。 创建一个配置,demo如下: 需要注意的有两个地方:
-
一个是选择想要使用的reCAPTCHA类型,目前有3种。显式复选框的v2版本,隐藏式的v2版本以及最新的v3版本。所以对于不同的版本,我们需要创建不同的配置。这三种类型的区别可以见下表:
显式v2 隐式v2 v3 是否显示验证框 ⭕ ❌ ❌ 何时验证 点复选框 提交 提交 验证返回数据 是否成功 是否成功 用户可信度(0-1) -
还有一个是域名,我们这边只是本地环境,所以用了
127.0.0.1
。如果需要支持线上环境,可以添加其他域名。
配置完可以拿到我们后续需要用到的一对密钥,一个是添加在html页面里面的public key,一个是后端验证时需要的secret key。
显式v2版本
从上面的图片不难看出html部分的代码很简单:
|
|
接下来根据 reCAPTCHA文档 我们需要引入js用来渲染验证复选框。js部分代码为:
|
|
url参数说明
除了渲染复选框可以指定参数外(代码里的注释部分),引用reCAPTCHA js的时候有3个可选参数。
- onload:加载完依赖项时的callback函数
- render:是否显式加载组件,默认值为onload,表示自动加载,也就是默认找到第一个class为g-recaptcha的标签来加载组件。例子中我们设置的值为explicit,意思是不启用自动加载,而是根据我们提供的DOM id进行加载。
- hl:语言,不设置的话自动检测浏览器语言作为标准,建议不设置。
验证流程
所以我们上面的代码就是当加载完js之后渲染验证复选框,当本地验证成功后显示验证成功的文字。这里的验证仅仅是本地验证,下一步呢就是服务端验证。具体的流程是,当本地验证成功后会产生一个token,我们需要把token发送到后端,后端把这个token发送到google的服务器,然后google服务器会返回一个验证结果,我们可以通过这个验证结果来判断用户是否通过验证。将token发送到后端这部分工作我们利用js完成:
|
|
服务端代码比较简单,python的话直接用requests库发post请求就行。
|
|
demo运行效果gif
demo运行效果可以在下面的gif看到:
上面一趟完整流程走下来,服务端会收到前端传来的post请求和google返回来的验证结果,都可以在console里打印出来看,为了方便观看,这里的token改了一下,可以看到验证结果为成功。
|
|
所以显式v2的版本仅仅本地验证成功是不行的,因为如果有人用Postman做了一个假post请求,token随便设置成"123"
的话就能绕过前端本地验证。
隐式v2版本
之前的显示v2版本有两个缺点:
- 用户在提交之前多了手动验证这一步,降低了用户体验。
- 本地验证因为可以随便绕过显得有点多余,是否可以将两步验证合并成一步。
为了改进上面的缺点,隐式v2版本选择在用户点击提交按钮的时候本地验证+google验证。这样做的好处是对于真实用户,图片验证可能不会被触发,所以用户感觉不到验证这一步体验不会下降,而机器人在提交的时候可能会被要求验证。
相比显式v2版本,代码改动主要有3个地方,详情浏览github demo:
- 渲染验证码的时候,size改成invisible。
- 把后端验证代码合并到verifyCallback里,之前本地验证和后端验证是分开的。
- submit监听事件函数中运行
grecaptcha.execute(widgetId)
来触发验证。
demo运行效果gif
v3版本
其实上面的隐式v2版本已经是非常不错了,但是还有一个问题,那就是验证通过与否全由google api来判断,用户没有一丁点决策权,过于霸道了。所以v3版本的改进是,对于不同组件(action),返回一个0-1范围内的用户真实度score,用户可以根据score来决定验证是否通过,默认阈值为0.5。与此同时,如下图所示我们也可以得到我们网站各个组件的验证通过比率和score分布,图片来自谷歌官网v3视频。
假设现在网站有两个组件需要验证,一个是用户登录,一个是评论。经过1个月的运行,发现用户登录组件的score分布中频率最高为0.7, 评论组件的频率最高score为0.4。那么我们或许可以根据判断来适当提高登录组件的score判定阈值来抵挡掉更多机器人的请求,或许可以适当降低评论组件的判定阈值来增加评论件数。
有两个地方需要代码改动,详情浏览github demo:
- 引入api.js的时候,render选项改成publick key。
- 渲染组件的代码精简了,在onsubmit监听函数中直接ready初始化, execute执行验证。
demo运行效果gif
可以看到对于我们定义的submit action,这次验证的分数为0.9。google api验证返回的内容是
|
|
总结
Google reCAPTCHA从v1版的文字验证,到v2版的图片显式/隐式验证,再到v3版的score验证,的确是解决了之前存在的问题,变得越来越好用了。Google内部靠什么来进行风险分析不会公开出来,可能会用到你的IP请求数,浏览器指纹,谷歌账号cookies之类的。当然Google reCAPTCHA不是万能的,肯定有方法可以hack,但是肯定会比较复杂,能够提高攻击者的攻击难度和成本我觉得这就足够了。以上。
如果本文对您有帮助,欢迎打赏。