Proof of work
每次验证都在 Caputchin 签发一个令牌之前,请访客的浏览器解一个小小的计算谜题。那个谜题就是 proof of work。
它如何运作
Caputchin 递给浏览器一个挑战,并请它找一个数(一个 nonce),使得当它和挑战一起被哈希时,产生一个以所要求数量的前导零开头的哈希。没有捷径:浏览器只能一个 nonce 接一个 nonce 地试,直到碰巧有一个管用。找到一个有效的 nonce 要试许多次;检查一个则是单次哈希。所以这份工作做起来难、核验起来却便宜,而 Caputchin 确认这个答案通过了所要求的难度。
Content-Security-Policy
求解器跑在一个 Web Worker 里,组件用一个 blob: URL 创建它,所以嵌入页的 Content-Security-Policy 必须为 worker 允许 blob:(worker-src blob:,或作为回退链的 child-src / default-src)。这一部分是必需的:没有主线程回退,所以一个挡住 worker 的策略会挡住验证。另外,求解器为速度编译一个 WebAssembly 例程,那需要 script-src 里的 'wasm-unsafe-eval'。那一部分是可选的:没有它,同一个求解器在 Worker 里以纯 JavaScript 跑得更慢。完整的宿主页策略见 游戏沙箱。
为什么它阻止滥用
要点不是让任何单次求解不可能。而是让大规模的滥用不划算。
一位真实访客在零点几秒内解一个谜题,根本察觉不到。一个想发数千个请求的机器人要把那份算力成本付上数千遍。如果通过挑战的成本超过了垃圾信息的价值,这次攻击就不再有经济意义。Proof of work 把目标从“无法攻破”转向“不值得”。
那些旋钮
一个密钥 安全 页上的两项设置调这个成本:
- 难度 设定哈希必须有多少个前导零,这抬高每次求解的成本。
- 每请求挑战数 通过一次要求好几次求解,把那个成本翻倍。
一个诚实的局限
没有挑战是无法攻破的;只要钱够多,任何东西都能被解。Proof of work 接受这一点,转而在成本上竞争。它也只证明了 耗费 了努力,而非一个真实的浏览器耗费了它,这正是 Caputchin 把它和 探测 配对、好把环境也检查一遍的原因。