← 返回首页

原理笔记 / cap.bug.im

不选图片,人机验证是怎么工作的?

点一下“验证”,稍等片刻,就能提交表单。没有辨认红绿灯,也没有填写扭曲的文字,网站凭什么放行?要理解 cap,可以先看看浏览器在这段时间里做了什么。

一次验证,先看全貌
浏览器领题

领取计算题
获取新题目

浏览器计算

逐个试数字
寻找答案

后端校验

先检查答案
再处理提交

网页上的“验证完成”只是一个中间状态。你的后端检查通过后,这次业务提交才应该继续。

一条评论,为什么需要验证?

想象一个开放的留言板。你偶尔写下一条评论,脚本却可能连续提交上千条。网站需要让反复提交这件事多花一点成本。

cap 采用的办法是:提交之前,让浏览器先完成一道计算题。单次计算带来一段等待;大量提交则需要不断重复计算。这个机制来自开源项目 ALTCHA,通常叫作工作量证明。

把流程拆开看,一次正常的提交会经过这些步骤:

  1. 先领一道题

    网页上的控件向 cap 请求挑战。题目里带着计算目标、有效期和所属站点。

  2. 由浏览器找到答案

    计算在访客自己的设备上进行。找到答案后,控件把结果随表单一起提交。

  3. 让你的后端检查结果

    后端向 cap 发起校验,确认题目、答案和站点是否匹配,再决定是否继续处理评论。

访客不需要登录 cap。需要登录的是网站开发者,用来创建站点、管理凭据和查看用量。

给开发者:接口与凭据分别是什么?
sitekey
站点的公开标识,填在前端控件上,不需要保密。
secret
后端校验使用的私有密钥,只能保存在服务端。
cap-response
控件写入表单的验证结果,常被称为 token。后端将它与 secret 一起发送到 /v1/siteverify,检查 success 是否为 true。

找答案费工夫,检查答案却很快

可以把它想成从一串钥匙里找出能开锁的那把。寻找时可能要试很多次;找到了以后,确认这把钥匙能不能开锁,只要再试一次。

真实题目里没有钥匙,而是一个数字和一种叫 SHA-256 的计算方法。它能把一段输入变成固定长度的结果。给定输入,算结果很方便;只知道结果,要反过来找到符合要求的数字,就需要尝试。

cap 会给出一段随机内容和一个目标结果。浏览器把不同数字接在随机内容后面计算,直到结果与目标相同。你可以用下面的简化示例看一遍:

动手看一看

在 32 个数字里找答案

把 cap-demo: 和一个数字拼起来,做一次 SHA-256。目标是找到一个 0~31 之间的数字,让结果与下面的目标一致。

给定的目标结果222791cfd5321aeb07fbcfb0af0a148dbb7ed825f52cf8607131c46851ed6431
—

当前尝试的数字
已试过 0 个候选数

先寻找,再检查。留意两步分别需要做多少次计算。

教学示例只在本页运行,不会生成真实验证凭据。显示过程特意放慢,不能用来衡量设备速度;真实挑战的随机内容、范围与完整校验流程也不同。

这就是它能增加成本的原因:服务端让浏览器先付出一些计算工作,自己再以较小的成本检查结果。

提交越多,
需要付出的计算成本也越多。

不过,脚本也会计算。有足够算力的自动化程序仍然能通过,所以这道题的作用是抬高滥用成本,不能用它确认对方的真实身份。

为什么有时很快,有时要多等一会儿?

原因通常有两类。首先,同一道题在不同设备上算得不一样快:旧手机、正在忙的电脑,可能需要更长时间。其次,cap 给不同请求的题目,难度也可能不同。

在发题之前,cap 会参考几类信号,决定这次计算应该设置多高的难度:

来源网络
请求来自普通网络,还是机房或托管网络?这只是一项参考,机房 IP 也可能由正常用户使用。
请求头
请求声称来自某种浏览器,携带的信息是否与它通常的行为一致?这些信息可以被模仿,也可能因环境而缺失。
威胁情报
来源 IP 是否出现在公开的风险列表中?cap 当前默认使用 FireHOL level 1 情报源。
看起来风险较低的请求
更可疑的请求
示意:风险分更高时,计算难度会提高。方块数量不代表固定的计算次数或等待时间。

这些线索共同影响风险分,但没有哪一项能直接认定一个人是机器人。风险分用于调整计算成本;实际等待多久,还要看设备速度和当时的运行状态。

如果算一次,再把答案交一百次呢?

如果这样就能通过,批量脚本只要算出一个答案,就可以不停复用。所以,检查答案正确还不够,题目本身也需要一些约束。

  • 题目会过期挑战带有有效期,过期后需要重新领题计算。题目的重要参数经过签名,客户端改动后无法按原签名通过检查。
  • 题目属于具体站点为 A 站点签发的结果,不能直接拿到 B 站点使用。后端校验会检查它与站点是否匹配。
  • 用过的结果会被记录后续校验查到已有记录时,会拒绝再次使用。这种检查通常称为“防重放”。

这里有一个实际限制:当前的边缘存储并非强一致。同一结果如果同时被不同节点处理,仍可能重复通过。因此,订单、奖励领取等不可重复执行的业务,仍需在自己的后端确保同一件事只处理一次,也就是做好“幂等”。

不看图片,也需要了解请求的来路

计算题之外,前面提到的风险判断会使用 IP、请求头和边缘节点提供的粗粒度网络信息。cap 不采集浏览器设备指纹,也不依赖跨站追踪来识别访客。

当前实现使用的数据
用途使用哪些信息
风险判断来源 IP、网络归属、部分请求头,以及边缘节点能提供的协议等信息。
用量统计记录发题、成功、失败和降级事件,包括时间与原因;完整访客 IP 仅记录在发题事件中。
控制台展示IP 展示时始终脱敏,例如 12.34.xx.xx;满 7 天后不再返回完整 IP。

完整 IP 满 7 天后进入匿名化范围,事件满 30 天后进入删除范围,后台按批次清理;维护任务异常或缺少流量时,物理清理可能滞后。控制台管理还会保存 GitHub ID、登录名、首次及最近登录时间和首次建站时间,不保存 GitHub 授权令牌。

把验证放进完整的防护流程里

人机验证可以增加批量提交的成本,但真浏览器自动化、住宅代理和有足够算力的脚本,仍然可能完成挑战。正常用户也可能因为设备慢或网络不稳定而遇到困难。

因此,接入时还要把下面几件事处理好:

  • 由后端确认结果。前端显示“已通过”后,服务端仍必须检查校验接口的返回值。
  • 给业务设置限制。例如限制同一账号的提交频率,并让不可重复的操作只执行一次。
  • 照顾验证失败的用户。提供明确提示与重试入口,不让一次失败变成无法继续的死胡同。

准备动手时,可以先在控制台创建站点,按首页的接入示例连接前端与后端,再到演示页调整控件样式。

国内外访问与备案信息

cap.bug.im 已启用阿里云 ESA 全球边缘加速,通过已完成 ICP 备案的 bug.im 域名提供服务。备案信息可通过页脚的工信部链接查询。

边缘加速用于改善资源加载和网络请求的访问体验。浏览器解题仍在访客设备上进行,所需时间取决于设备性能与题目难度。

已经接入 Turnstile,迁移时需要注意什么?

cap 的校验接口使用相近的请求字段与成功结果结构,但仍需替换脚本、控件、接口地址和凭据,并检查失败处理。接口形式相近,不代表所有行为都可以原样替换。

cap 的计算挑战与验证控件基于开源项目 ALTCHA。cap 在此基础上提供站点管理、域名限制、请求风险判断与用量统计。

想继续了解协议和控件的实现,可以阅读 ALTCHA 项目。

了解原理后,试试实际的控件。

打开演示页 →