Explainers

隐形验证码系统解释:它们是如何工作的

页面上找不到验证码控件,脚本却还是被拦下来——这种情况十有八九是撞上了隐形验证码。它不需要你点复选框,不需要解九宫格图片,也不需要输入文字。

整个验证过程都在后台由 JavaScript 完成:分析浏览器行为、设备特征、网络信誉,悄悄给这次访问打一个分。自动化流程如果遇到一个表单,某些请求顺利提交、另一些却莫名其妙被挡,而页面上从头到尾都没出现过验证码,那大概率就是隐形验证码在起作用。


隐形验证码的工作原理

不管具体是哪家厂商,隐形验证码的核心流程都差不多:

Page loads → CAPTCHA JavaScript injected
    ↓
Script runs in background, collecting signals:
  ├─ Browser fingerprint (canvas, WebGL, fonts, plugins)
  ├─ Behavioral data (mouse, keyboard, scroll, timing)
  ├─ Network data (IP reputation, TLS fingerprint)
  ├─ Device data (screen, GPU, CPU cores, memory)
  └─ History data (cookies, previous CAPTCHA completions)
    ↓
Signals sent to CAPTCHA provider's risk engine
    ↓
Risk score computed
    ↓
Response varies by score:
  ├─ Low risk  → Token issued silently (user sees nothing)
  ├─ Medium    → Light challenge shown (checkbox, simple click)
  └─ High risk → Full challenge (image grid) or block

和可见验证码最大的区别在这里:验证从页面加载那一刻就已经持续运行,而不是等用户点了复选框才开始。


快速对比:几种隐形验证码怎么选

在逐个拆解之前,先看一眼几种主流实现的差异,方便按场景挑选:

维度 reCAPTCHA v3 Turnstile reCAPTCHA v2 隐形 hCaptcha 被动
是否显示可见挑战 从不 很少 有时(兜底) 很少
隐私影响 采集浏览数据 不收集个人数据 采集浏览数据 采集交互数据
费用 免费(每月 100 万次) 免费(不限量) 免费(每月 100 万次) 仅企业版
是否返回分数 是(0.0–1.0) 否(通过/不通过) 否(通过/不通过) 是(企业版)
兜底可见挑战 网站自行决定 由 Cloudflare 管理 弹出图片验证 弹出图片验证
CaptchaAI 是否支持 支持(识别 v3 token) 支持(识别成功率高) 支持(识别 token) ❌ 不支持

主流隐形验证码逐个拆解

1. reCAPTCHA v3:Google 的评分模型

  • 提供商: Google
  • 上线时间: 2018 年
  • 市场份额: 隐形验证码里占比最高

reCAPTCHA v3 不设及格线,而是给每一次页面操作打一个 0.0 到 1.0 的浮点分数,具体怎么处理这个分数,完全由网站自己决定;信号从脚本加载那一刻就开始收集:

<!-- reCAPTCHA v3 integration -->
<script src="https://www.google.com/recaptcha/api.js?render=SITE_KEY"></script>
<script>
  grecaptcha.ready(function() {
    grecaptcha.execute('SITE_KEY', { action: 'login' }).then(function(token) {
      // Token sent to server for verification
      document.getElementById('captcha-token').value = token;
    });
  });
</script>

execute() 只是按需触发一次信号打包和评分请求,脚本本身早已在后台默默采集数据。分数对照表如下:

分数 含义 常见处理方式
0.9 很可能是真人 放行
0.7 大概率是真人 放行并记录监控
0.5 不确定 要求额外验证
0.3 大概率是脚本 拦截,或降级到 reCAPTCHA v2
0.1 极可能是脚本 直接拦截

从页面源码判断是否用了 reCAPTCHA v3:

import requests
import re

def detect_recaptcha_v3(url):
    html = requests.get(url, timeout=10).text
    indicators = {
        "recaptcha_v3": False,
        "site_key": None,
        "actions": [],
    }

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

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

    return indicators

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

2. Cloudflare Turnstile:增长最快的隐形验证码

  • 提供商: Cloudflare
  • 上线时间: 2022 年
  • 市场份额: 增长最快

Turnstile 走的是一套多阶段校验流程,整个过程不需要收集用户的个人数据,具体分这几步:

  • Private Access Token 校验 — 浏览器如果支持 Apple PAT,验证几乎瞬间完成
  • 非交互式挑战 — 浏览器在后台完成一次轻量级的加密工作量证明
  • 托管挑战 — Cloudflare 根据自家网络看到的风险信号(超过 20% 的互联网流量都经过 Cloudflare)决定是否需要用户交互
  • 交互式挑战 — 只有风险分数偏高时才会出现,样式是一个类似复选框的小控件

接入示例:

<!-- Turnstile widget -->
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

检测方法:

def detect_turnstile(url):
    html = requests.get(url, timeout=10).text
    indicators = {
        "turnstile": False,
        "site_key": None,
    }

    if "challenges.cloudflare.com/turnstile" in html or "cf-turnstile" in html:
        indicators["turnstile"] = True
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            indicators["site_key"] = match.group(1)

    return indicators

3. reCAPTCHA v2 隐形模式

  • 提供商: Google
  • 上线时间: 2017 年

这本质上还是 reCAPTCHA v2,只不过改成了在表单提交时以编程方式触发——页面上看不到复选框,用户点提交按钮后才在后台跑起来,风险分数偏高时依旧会弹出图片验证挑战:

<button class="g-recaptcha"
        data-sitekey="SITE_KEY"
        data-callback="onSubmit"
        data-size="invisible">
    Submit
</button>

和 v3 的核心区别: v2 隐形模式在风险用户面前依旧会兜底弹出可见挑战;v3 从不展示任何挑战——分数拿到之后要怎么处理,是网站自己的判断。

4. hCaptcha 被动模式(仅企业版)

  • 提供商: Intuition Machines
  • 可用范围: 仅 Enterprise 版本

hCaptcha Enterprise 的被动模式在页面上不放任何控件就能给用户打分,采集的行为信号和标准版一致,只对最可疑的那批访问者才弹出可见挑战。

5. Apple 私有访问令牌(Private Access Token)

  • 提供商: Apple
  • 支持范围: iOS 16+、macOS Ventura+

这并不是验证码产品,而是一种设备认证机制:iOS/macOS 设备通过硬件认证向网站证明自己是真机,网站核验通过后就跳过验证码环节。

Device attestation:

  1. Website requests a challenge from a token issuer
  2. Device proves it is genuine without revealing identity
  3. Token issuer generates a blind-signed token
  4. Website verifies token → CAPTCHA skipped

Cloudflare、Fastly 等 CDN 厂商都已经支持这套机制。


用 CaptchaAI 拿到隐形验证码的 token

隐形验证码依然会产出一个必须随表单一起提交的 token,基于 API 的识别服务就是在外部把这个 token 算出来。

举个例子:某跨境电商团队要给面向海外用户的注册页做自动化回归测试,页面用的是 reCAPTCHA v3——脚本要连接 Google 域名,国内网络下常常加载缓慢甚至超时,直接调用 CaptchaAI API 拿 token 会稳定得多,也不用把测试节点特意搬到海外。

识别 reCAPTCHA v3

import requests
import time

API_KEY = "YOUR_API_KEY"

submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "6LdKlZEpAAAAAAOQjzC2v_d36tWxCl6dWsozdSy9",
    "pageurl": "https://staging.example.com/qa-login",
    "version": "v3",
    "action": "login",
    "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"reCAPTCHA v3 token received (score ≥ 0.9)")
        break

识别 Cloudflare Turnstile

submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "turnstile",
    "sitekey": "0x4AAAAAAAC3DHQhMMQ_Rxrg",
    "pageurl": "https://example.com/signup",
    "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:
        turnstile_token = result["request"]
        print(f"Turnstile token received")
        break

Node.js:识别 reCAPTCHA v3

const axios = require("axios");

async function solveRecaptchaV3(siteKey, pageUrl, action) {
    const API_KEY = "YOUR_API_KEY";

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

    const taskId = submit.request;

    // Poll
    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("Solve timeout");
}

solveRecaptchaV3("SITE_KEY", "https://staging.example.com/qa-login", "login")
    .then((token) => console.log("Token:", token.substring(0, 50) + "..."));

检测页面里的隐形验证码:一段通用脚本

把上面几种检测逻辑合并成一个函数,跑一次就能知道当前页面用了哪种隐形验证码:

import requests
import re

def detect_invisible_captcha(url):
    """Detect all invisible CAPTCHA types on a page."""
    html = requests.get(url, timeout=10).text

    results = []

    # reCAPTCHA v3
    if "recaptcha/api.js?render=" in html:
        key = re.search(r"render=([A-Za-z0-9_-]+)", html)
        results.append({
            "type": "reCAPTCHA v3",
            "site_key": key.group(1) if key else "unknown",
            "solver_method": "userrecaptcha (version=v3)",
        })

    # reCAPTCHA v2 invisible
    if 'data-size="invisible"' in html and "g-recaptcha" in html:
        key = re.search(r'data-sitekey="([^"]+)"', html)
        results.append({
            "type": "reCAPTCHA v2 Invisible",
            "site_key": key.group(1) if key else "unknown",
            "solver_method": "userrecaptcha (invisible=1)",
        })

    # Cloudflare Turnstile
    if "challenges.cloudflare.com/turnstile" in html:
        key = re.search(r'data-sitekey="([^"]+)"', html)
        results.append({
            "type": "Cloudflare Turnstile",
            "site_key": key.group(1) if key else "unknown",
            "solver_method": "turnstile",
        })

    # hCaptcha (passive mode detected by script without visible widget)
    if "hcaptcha.com/1/api.js" in html and 'data-size="invisible"' in html:
        results.append({
            "type": "hCaptcha Passive",
            "solver_method": "hcaptcha",
        })

    if not results:
        results.append({"type": "none detected"})

    return results

for item in detect_invisible_captcha("https://example.com"):
    print(item)

评分策略示例

  • 在流程正式上生产流量之前,先明确高分、中分、低分各自要触发什么动作。
  • 中等分数的结果用来触发升级检查即可,不必把每一个没达标的分数都当成失败处理。
  • 持续记录分数分布,这样阈值随时间漂移时能提前发现,而不是等转化率掉下来才察觉。

常见问题

页面上没有验证码控件,怎么判断它用的是隐形验证码?

查看页面源码里有没有验证码厂商的脚本:recaptcha/api.js?render=(reCAPTCHA v3)、challenges.cloudflare.com/turnstile(Turnstile),或者同时出现 hcaptcha.com/1/api.jsdata-size="invisible"(hCaptcha 被动模式)。

隐形验证码没有可见控件,只能靠检查 HTML 或网络请求确认。

reCAPTCHA v3 的分数在国内网络环境下测试起来时好时坏,是什么原因?

reCAPTCHA v3 的脚本需要连接 Google 的域名,国内网络访问不太稳定,加载慢甚至失败都会拉低分数。

测试面向海外用户的页面时,把测试节点放在海外,或者直接用 CaptchaAI API 拿 token,能避开这个不稳定因素。

自动化脚本被隐形验证码拦截了,为什么控制台没有报错?

表单提交里没带上有效 token,服务器通常会静默拒绝请求,或只返回一个笼统的错误信息,不会弹出验证码提示。

请求失败往往是因为 token 缺失或已失效,需要专门检查响应内容,而不是只看有没有报错。

Cloudflare Turnstile 和 reCAPTCHA v3 该怎么选?

站点如果对隐私合规敏感,又不想给用户增加交互步骤,Turnstile 更合适:不采集个人数据,额度不限量。

已经在用 Google 生态、需要连续风险分数做策略分层的,reCAPTCHA v3 更灵活。两者 CaptchaAI 都能识别,实际项目也可以按场景混用。


总结

隐形验证码通过后台 JavaScript 分析浏览器行为、设备特征和网络信誉来判断真人还是脚本,全程不需要任何可见交互。reCAPTCHA v3 和 Cloudflare Turnstile 是目前最主流的两种实现,Turnstile 凭借免费额度和隐私优先的设计增长最快。

对自动化脚本来说,隐形验证码依然要求一个有效 token 才能通过表单校验。用 CaptchaAIuserrecaptcha 方法加 version=v3 参数识别 reCAPTCHA v3,用 turnstile 方法识别 Cloudflare Turnstile,识别成功率高。

相关文章

该文章已禁用评论。