如何提升 reCAPTCHA v3 分数:六个切实有效的方法

improve recaptcha v3 score - How to Improve reCAPTCHA v3 Score: Six Fixes That Work

你无法直接设定 reCAPTCHA v3 的分数。Google 会按每个请求给出分数,你的代码无法直接改变它。你能改变的是影响分数的因素:如何命名 action、何时请求 token、网站有多少部分运行了 reCAPTCHA,以及后端如何处理返回结果。改进这些,分数分布就会随之变化。

六项改动,按通常的效果大小排序。先做测量,因为在自认为存在分数问题的网站中,有一半其实是阈值问题。

分数到底意味着什么

v3 会在每次验证时返回一个介于 0.0 和 1.0 之间的数值。Google 的表述是:1.0 极有可能是正常交互,0.0 极有可能是机器人。这里没有复选框,也没有拼图,所以这个数字就是全部信号。

由此可以得出两点,且都很重要:

  • 分数是 按每个请求、每个 action 计算的,而不是按用户计算。同一位访客在你的首页可能得 0.9 分,在结账页却可能得 0.3 分。
  • Google 建议的初始阈值是 0.5。这是一个供你逐步调整的默认值,而非需要达成的目标。

如果你还不清楚 v3 与复选框版本有何不同,我们关于 reCAPTCHA 工作原理 的说明介绍了其运作机制。

在改动任何东西之前先做测量

reCAPTCHA 管理控制台会显示你网站的分数分布,以及排名前十的 action 的细分数据。在动代码之前先看看它。你要找的是以下三种形态之一:

  • 全部集中在 0.9,但你仍在拦截真实用户。问题出在你的阈值或后端逻辑,而不是分数。
  • 分布很分散 ,并在低分端有一个小高峰。这很正常。按每个 action 调整阈值即可。
  • 全部集中在 0.1 到 0.3。 说明存在结构性问题。通常是 token 的问题,而非流量本身。

Google 还提醒,测试环境中或刚安装 v3 之后的分数会与生产环境不同,因为模型尚无该网站的历史数据。请先积累一周的真实流量再下结论。若要单独核验某个请求,我们的 reCAPTCHA v3 实时测试页面 会返回单次识别的原始分数。

方法一:为你的 action 命名,并正确命名

这是单项收益最大的做法,却常被忽略。Google 会分别为每个 action 打分,并以该 action 自身的历史作为上下文。整个网站只用一个通用 action,就意味着只有一份混杂的历史,每个页面都会继承其中最差的部分。

// One action per meaningful event. Not one for the whole site.
grecaptcha.ready(function () {
  grecaptcha.execute("YOUR_SITEKEY", { action: "login" })
    .then(function (token) {
      document.getElementById("recaptcha-token").value = token;
    });
});

Google 强制执行的规则:action 只能包含字母数字字符、斜杠和下划线,且不得针对特定用户。因此 checkout/payment 是可以的, checkout_user_8842 则不行。针对特定用户的 action 会把历史拆分成数千个几乎都没有数据的分桶,这比完全不命名 action 还要糟糕。

方法二:不要只在表单上运行 reCAPTCHA

v3 对行为打分,而行为需要不止一个数据点。如果脚本只在登录页加载,Google 看到的就是一位凭空出现在表单前并提交的访客,而这恰恰是机器人的典型表现。

Google 自己的建议是在整个网站范围内加载 v3,包括没有表单的页面。你不必在这些页面上进行验证。仅仅执行脚本,就能让模型在访客到达你关注的 action 时已有可参考的数据。

这也是人们常常无意中撤销的一项改动。把脚本放进只在结账路由触发的条件判断中,会在几天内拉低你的分数。

方法三:在提交时获取 token,而非在页面加载时

reCAPTCHA v3 的 token 在签发两分钟后失效。如果你在 ready() 中于页面加载时生成 token,那么任何阅读表单时间超过两分钟的访客提交的都是失效的 token。取决于后端如何处理这种失败,它会表现为低分或直接被拒绝。

// Solve on submit so the token is always fresh.
form.addEventListener("submit", function (e) {
  e.preventDefault();
  grecaptcha.execute("YOUR_SITEKEY", { action: "login" })
    .then(function (token) {
      tokenField.value = token;
      form.submit();
    });
});

长表单、多步骤结账以及任何涉及文件上传的场景,受此影响最为严重。

方法四:在服务器端验证,并同时检查 action

在后端完成交换之前,token 毫无价值。这次交换会返回分数、action 和一个 hostname,这三者你都应该检查。

# Exchange the token for the score. Server side only.
curl -X POST https://www.google.com/recaptcha/api/siteverify \
  -d secret=YOUR_SECRET_KEY \
  -d response=THE_TOKEN_FROM_THE_PAGE

# {"success":true,"score":0.9,"action":"login","hostname":"example.com"}

如果你只检查 success,那你根本没在使用 v3。 success 只说明 token 解析成功,并不代表访客像是真人。而如果你不将 action action 与该端点所预期的值进行比对,那么在你低价值的邮件订阅表单上生成的 token,就能在你的登录端点上照样通过。

方法五:为每个 action 分别设定阈值,而不是全站统一

结账和邮件订阅不应共用同一个临界值。等每个 action 积累了一周数据后,再根据你实际流量的分布分别设定。

Action 类型合理的起始值低于该值时怎么办
邮件订阅、搜索、页面浏览0.3放行,并记录分数
登录、评论0.5增加二次验证或 v2 复选框
结账、密码重置0.7升级为人工挑战

注意,以上各行没有一处写着“拦截”。对低分直接硬性拦截,正是 v3 部署把使用企业 VPN 和隐私浏览器的真实客户拒之门外的原因。应改为升级挑战难度。

方法六:排查常见的分数杀手

如果结构性改动都已到位,分数却依然偏低,请逐项排查以下问题:

原因为何会导致低分
同一页面加载了两个 reCAPTCHA 脚本第二次加载会覆盖第一次,导致 token 绑定到错误的上下文
共享 IP 或数据中心 IP办公室 NAT、VPN 和云出口都带有他人的历史记录
激进的隐私保护扩展被拦截的 cookie 和存储让模型无从读取任何数据
以 iframe 或嵌入方式加载的表单跨域上下文会削弱信号
sitekey 与域名不匹配检查 hostname 字段,它位于验证响应中。

有一件事 并不 在这份清单上:隐藏徽标。这是一个 CSS 和署名的问题,对分数没有任何影响。我们在 隐藏 reCAPTCHA v3 徽标.

调优也解决不了的问题

如果流量是自动化的,它得分低,正是因为 v3 就是为检测这种流量而设计的。再怎么命名 action 也救不了无头浏览器。若要测试你自己的网站,或运行你获得授权的自动化任务,可以从识别工具而非页面获取 token:

# pip install capskip
from capskip import CapSkip

solver = CapSkip(host="127.0.0.1", port=8080)

result = solver.recaptcha(
    sitekey="YOUR_SITEKEY",
    url="https://example.com/page-with-recaptcha",
    version="v3",
    action="login",     # must match the page
)

print(result["code"])   # token, inject it and submit

同样要注意其中的 action 。它在这一侧和你那一侧同样重要,原因完全一样:后端会对它进行比对。

你无法做到的是指定一个分数。识别请求上没有最低分数参数。这个数字由 Google 决定,在你的 token 被验证时给出,因此识别工具只会交给你一个 token,仅此而已。

常见问题

为什么我的 reCAPTCHA v3 分数总是 0.1?

所有流量都平平地停在 0.1,几乎从来不是行为问题。请检查是否有重复的脚本标签、是否使用了在提交前两分钟以上就已签发的过期 token,或者 sitekey 是否注册到了不同的域名。验证响应中的 hostname 字段能立即确认最后一种情况。

分数变化需要多久才能显现?

需要几天。模型按网站和 action 使用历史数据,因此新的 action 起初没有上下文,会随着流量积累而逐渐稳定。不要凭一个下午的数据来评判一项改动。

更高的分数阈值会让我的网站更安全吗?

只在一定程度上有用,而且会让你损失真实用户。把每个端点都提高到 0.9,早在挡住铁了心的攻击者之前,就会先把使用共享 IP 和隐私浏览器的用户拒之门外。应按每个 action 设定阈值并升级挑战难度,而不是直接拒绝。

不写后端代码也能看到分数吗?

可以。管理控制台会显示你自己网站的分数分布,而我们的 v3 演示页面会返回单次识别的原始分数,方便你将某个请求与你自己端点报告的结果进行对比。

小结

为每个事件命名一个 action,在全站加载 v3,在提交时才生成 token,在服务器端验证并比对 action,然后为每个端点分别设定阈值,而不是使用一个全局临界值。一周后再查看管理控制台,而不是当天。

有关 v3 评分机制及相关选项,请参阅 reCAPTCHA v3 识别,以及 Google 官方的 v3 文档 ,这是关于阈值和 action 命名的权威资料。如果你正针对低分测试自己的表单,CapSkip 是一款 验证码识别工具 它在本地运行,因此你可以整天生成 token,而不必按次付费。