原理笔记 / cap.bug.im
不选图片,人机验证是怎么工作的?
点一下“验证”,稍等片刻,就能提交表单。没有辨认红绿灯,也没有填写扭曲的文字,网站凭什么放行?要理解 cap,可以先看看浏览器在这段时间里做了什么。
领取计算题
获取新题目
逐个试数字
寻找答案
先检查答案
再处理提交
一条评论,为什么需要验证?
想象一个开放的留言板。你偶尔写下一条评论,脚本却可能连续提交上千条。网站需要让反复提交这件事多花一点成本。
cap 采用的办法是:提交之前,让浏览器先完成一道计算题。单次计算带来一段等待;大量提交则需要不断重复计算。这个机制来自开源项目 ALTCHA,通常叫作工作量证明。
把流程拆开看,一次正常的提交会经过这些步骤:
- 先领一道题
网页上的控件向 cap 请求挑战。题目里带着计算目标、有效期和所属站点。
- 由浏览器找到答案
计算在访客自己的设备上进行。找到答案后,控件把结果随表单一起提交。
- 让你的后端检查结果
后端向 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 的校验接口使用相近的请求字段与成功结果结构,但仍需替换脚本、控件、接口地址和凭据,并检查失败处理。接口形式相近,不代表所有行为都可以原样替换。
了解原理后,试试实际的控件。
打开演示页 →