Explainers

reCAPTCHA Enterprise 评估 API 详解:分数原因与自动化识别

reCAPTCHA Enterprise 升级的是服务端那份评估报告,不是用户看到的验证码。站点运营方调评估 API 能读到分数、原因码和账号标签;自动化侧收到的仍旧只是一串 token,集成流程不必重写。把这条界线画清楚,分数为什么停在 0.1、token 明明有效却被拦,就都有了排查方向。


Enterprise 评估 API 的完整链路

前端只负责产出 token,判断发生在服务端:

Client-side:

  1. Load reCAPTCHA Enterprise script
  2. Call grecaptcha.enterprise.execute(SITE_KEY, {action: 'LOGIN'})
  3. Receive token
  4. Send token to your backend

Server-side:

  1. Create assessment via Enterprise API
  2. Receive detailed risk analysis
  3. Make access decision based on score + reasons
  4. Optionally annotate the assessment (report fraud/legitimate)

token 本身不带分数——它在服务端换评估结果时才生成,同一串 token 在不同站点、不同 action 下结论可能完全不同。


reCAPTCHA Enterprise 与标准 v3 有哪些实际差别

多出来的能力集中在服务端:

对比项 reCAPTCHA v3(免费) reCAPTCHA Enterprise
评分 0.0–1.0 分 0.0–1.0 分 + 分数原因
风险分析 基础 详细(欺诈信号、账号信息)
分数原因 不提供 给出具体原因码
Account Defender 有(跟踪账号生命周期)
WAF 集成 有(Cloudflare、Fastly、F5)
快速评估 有(纯服务端,不需要 JS)
密码泄露检测
定价 免费(每月 100 万次评估) 每 1000 次评估 $1(前 100 万次免费)
API 接口 google.com/recaptcha/api/siteverify recaptchaenterprise.googleapis.com

国内团队碰到 Enterprise 多半是在出海业务上:本土站点常用极验(GeeTest)、腾讯防水墙,海外 SaaS 的注册登录页才大量出现 Enterprise。


前端接入:enterprise.js 与 grecaptcha.enterprise

JavaScript SDK 的写法

<script src="https://www.google.com/recaptcha/enterprise.js?render=SITE_KEY"></script>
<script>
    grecaptcha.enterprise.ready(function() {
        grecaptcha.enterprise.execute('SITE_KEY', { action: 'LOGIN' })
            .then(function(token) {
                // Send token to backend
                fetch('/api/verify', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ token: token })
                });
            });
    });
</script>

与标准 v3 相比只有三处不同:

  • 脚本地址是 .../recaptcha/enterprise.js,不是 .../recaptcha/api.js
  • 调用对象是 grecaptcha.enterprise,不是 grecaptcha
  • execute() 返回的 token 格式完全一样

从页面源码判断版本

不用开发者工具,抓一次 HTML 就够:

import requests
import re

def detect_recaptcha_enterprise(url):
    """Detect if a page uses reCAPTCHA Enterprise."""
    html = requests.get(url, timeout=10).text

    indicators = {
        "is_enterprise": False,
        "is_standard": False,
        "site_key": None,
        "actions": [],
    }

    # Enterprise detection
    if "recaptcha/enterprise.js" in html:
        indicators["is_enterprise"] = True
        match = re.search(r"render=([A-Za-z0-9_-]+)", html)
        if match:
            indicators["site_key"] = match.group(1)

    # Standard v3 detection
    elif "recaptcha/api.js?render=" in html:
        indicators["is_standard"] = True
        match = re.search(r"render=([A-Za-z0-9_-]+)", html)
        if match:
            indicators["site_key"] = match.group(1)

    # Extract action names
    actions = re.findall(r"action:\s*['\"](\w+)['\"]", html)
    indicators["actions"] = list(set(actions))

    return indicators

print(detect_recaptcha_enterprise("https://staging.example.com/qa-login"))

actions 一并抽出来是有意为之,它必须与站点实际发送的值一致。依赖装不上时走国内镜像:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests


服务端:调用评估 API 创建 assessment

这一节属于站点运营方:需要 Google Cloud 项目与权限,仅测试自有集成时用得上。

创建评估

from google.cloud import recaptchaenterprise_v1
from google.cloud.recaptchaenterprise_v1 import Assessment

def create_assessment(project_id, site_key, token, action):
    """Create a reCAPTCHA Enterprise assessment."""
    client = recaptchaenterprise_v1.RecaptchaEnterpriseServiceClient()

    event = recaptchaenterprise_v1.Event()
    event.site_key = site_key
    event.token = token
    event.expected_action = action

    assessment = recaptchaenterprise_v1.Assessment()
    assessment.event = event

    request = recaptchaenterprise_v1.CreateAssessmentRequest()
    request.assessment = assessment
    request.parent = f"projects/{project_id}"

    response = client.create_assessment(request)
    return response

评估响应的结构

{
    "name": "projects/123456/assessments/abcdef123",
    "event": {
        "token": "...",
        "siteKey": "6Le...",
        "expectedAction": "LOGIN",
        "hashedAccountId": "abc123..."
    },
    "riskAnalysis": {
        "score": 0.9,
        "reasons": [
            "AUTOMATION",
            "TOO_MUCH_TRAFFIC"
        ],
        "extendedVerdictReasons": [
            "BROWSER_ERROR"
        ]
    },
    "tokenProperties": {
        "valid": true,
        "hostname": "example.com",
        "action": "LOGIN",
        "createTime": "2025-01-15T10:30:00Z",
        "invalidReason": ""
    },
    "accountDefenderAssessment": {
        "labels": ["PROFILE_MATCH"]
    }
}

三个字段最值得看:

  • riskAnalysis.score — 0.0 到 1.0,越低越可疑
  • riskAnalysis.reasons — 分数为什么低,Enterprise 独有
  • tokenProperties.valid — 返回 false 时先看 invalidReason,多半是过期或域名不符

Account Defender:账号维度的风险标签

Account Defender 把判断从单次请求扩展到账号的整个生命周期:

{
    "accountDefenderAssessment": {
        "labels": [
            "PROFILE_MATCH",
            "SUSPICIOUS_LOGIN_ACTIVITY",
            "SUSPICIOUS_ACCOUNT_CREATION",
            "RELATED_ACCOUNTS_NUMBER_HIGH"
        ]
    }
}
标签 含义
PROFILE_MATCH 行为与该账号的已知画像一致
SUSPICIOUS_LOGIN_ACTIVITY 登录模式异常(新设备、新地区)
SUSPICIOUS_ACCOUNT_CREATION 注册行为看起来是自动化的
RELATED_ACCOUNTS_NUMBER_HIGH 多个账号关联到同一设备或会话

这些标签只对站点运营方可见,且要传 hashedAccountId 才会产生。


读懂分数原因:reasons 字段说明了什么

原因码 说明 分数影响
AUTOMATION 检测到自动化 user agent 或无头浏览器 -0.3 至 -0.7
UNEXPECTED_ENVIRONMENT 浏览器或设备环境不一致 -0.2 至 -0.4
TOO_MUCH_TRAFFIC 同一 IP 或会话请求量过高 -0.1 至 -0.3
UNEXPECTED_USAGE_PATTERNS 行为信号偏离常见人类模式 -0.2 至 -0.5
LOW_CONFIDENCE_SCORE 数据不足,无法给出可信判断 不固定
SUSPECTED_CARDING 交易模式接近信用卡欺诈特征 -0.3 至 -0.6
SUSPECTED_CHARGEBACK 基于交易信号判断的拒付风险 -0.2 至 -0.4

分数影响为量级参考,并非 Google 公布的固定权重,请以自有环境观测为准。

扩展判定原因(extendedVerdictReasons)

原因码 说明
BROWSER_ERROR 验证码 SDK 里的 JavaScript 执行报错
SITE_MISMATCH token 是为另一个站点生成的
FAILED_TWO_FACTOR 近期有双因素认证失败记录

SITE_MISMATCH 最容易踩:pageurl 写得不准,token 就会被打上这个标记。


WAF 边缘集成:Cloudflare 与 F5 BIG-IP

Enterprise 也能挂在 WAF 上,让挑战发生在到达源站之前:

Cloudflare WAF

Request arrives at Cloudflare edge
    ↓
Cloudflare WAF rule evaluates request
    ↓
Rule triggers reCAPTCHA Enterprise challenge
    ↓
Client solves CAPTCHA → token returned
    ↓
Cloudflare validates token via Enterprise API
    ↓
If valid + score above threshold → request forwarded to origin

F5 BIG-IP

F5 iRule or policy evaluates request
    ↓
Triggers reCAPTCHA Enterprise challenge page
    ↓
Client solves → token validated server-side
    ↓
F5 forwards or blocks based on assessment score

这类部署要先过挑战页才看到表单,pageurl 应填承载挑战的那个地址。


自动化场景:用 CaptchaAI 识别 reCAPTCHA Enterprise

求解侧把 Enterprise 当同一种 token 处理:方法名仍是 userrecaptcha,只多传一个标志位。

Python

import requests
import time

API_KEY = "YOUR_API_KEY"

# Enterprise is solved with the same method
# The solver handles the Enterprise variant automatically
submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp",
    "pageurl": "https://enterprise-site.com/login",
    "enterprise": 1,  # Flag for Enterprise variant
    "json": 1,
})

task_id = submit.json()["request"]

for _ in range(60):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY,
        "action": "get",
        "id": task_id,
        "json": 1,
    }).json()

    if result.get("status") == 1:
        token = result["request"]
        print(f"Enterprise token: {token[:50]}...")
        break

Node.js

const axios = require("axios");

async function solveEnterprise(sitekey, pageurl) {
    const API_KEY = "YOUR_API_KEY";

    const { data: submit } = await axios.post(
        "https://ocr.captchaai.com/in.php",
        new URLSearchParams({
            key: API_KEY,
            method: "userrecaptcha",
            googlekey: sitekey,
            pageurl: pageurl,
            enterprise: 1,
            json: 1,
        })
    );

    const taskId = submit.request;

    for (let i = 0; i < 60; i++) {
        await new Promise(r => setTimeout(r, 5000));
        const { data: result } = await axios.get(
            "https://ocr.captchaai.com/res.php",
            { params: { key: API_KEY, action: "get", id: taskId, json: 1 } }
        );

        if (result.status === 1) return result.request;
    }

    throw new Error("Timeout");
}

把版本判断收敛成一个函数

写死版本迟早出问题——站点升级到 Enterprise 不会通知你:

def identify_recaptcha_version(html):
    """Determine which reCAPTCHA version a page uses."""
    if "recaptcha/enterprise.js" in html:
        return "enterprise"
    elif "recaptcha/api.js?render=" in html:
        return "v3"
    elif "g-recaptcha" in html and 'data-size="invisible"' in html:
        return "v2_invisible"
    elif "g-recaptcha" in html:
        return "v2"
    else:
        return "none"

CaptchaAI 按线程计费,不按次计费:BASIC $15/月 5 线程、STANDARD $30/月 15 线程、ADVANCE $90/月 50 线程,套餐内识别次数不限,Enterprise 不加价。


reCAPTCHA Enterprise 排错对照表

现象 原因 处理方式
Enterprise 站点提交后 token 被拒 用标准方法提交了 Enterprise 页面 请求里补上 enterprise=1
token 有效,分数却长期停在 0.1 action 与页面发送的值不一致 逐字核对页面里的 action
reasons 里出现 AUTOMATION 站点判定运行环境为自动化 CaptchaAI 侧会处理,持续出现请联系支持
token 校验通过,请求仍被拦 站点还有验证码之外的检查 排查 WAF 规则、限流等风控层

常见问题

CaptchaAI 支持 reCAPTCHA Enterprise 吗?怎么计费?

支持,v2 Enterprise 与 v3 Enterprise 都在正式支持范围内,用 userrecaptchaenterprise=1 提交即可。计费按线程算,BASIC $15/月 5 线程,识别次数不限。

在第三方站点上,我能看到分数和原因码吗?

看不到。riskAnalysis 只有站点运营方调评估 API 才拿得到,求解侧只收到 token。只有测自己的集成时,才能把分数和 reasons 串起来看。

提交任务时漏了 enterprise=1 会怎样?

多数情况下仍会返回格式正确的 token,但站点校验不通过,看着很像“识别质量差”。判断标准很简单:页面加载 enterprise.js 就带,加载 api.js 就别带。

国内网络下调试 Enterprise 页面加载不出来,是集成写错了吗?

多半不是。前端脚本由 Google 域名提供,境内可达性不稳定,加载超时与 sitekey、action 无关。把复现环境放到境外服务器或 CI 上再跑一次即可确认。识别请求本身走 CaptchaAI 的接口,不受此影响。

用自动化处理验证码,合规上要注意什么?

只采集你有权采集的数据,并留意网络安全法、数据安全法、PIPL 以及目标站点的 robots 协议。技术跑得通不等于合规。


小结

Enterprise 补的全是服务端的“解释能力”,token 格式没变。自动化侧只需三件事:确认页面加载 enterprise.js、给 CaptchaAI 请求补上 enterprise=1、让 pageurlaction 对齐。

相关文章

该文章已禁用评论。