页面上找不到验证码控件,脚本却还是被拦下来——这种情况十有八九是撞上了隐形验证码。它不需要你点复选框,不需要解九宫格图片,也不需要输入文字。
整个验证过程都在后台由 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.js 和 data-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 才能通过表单校验。用 CaptchaAI 的 userrecaptcha 方法加 version=v3 参数识别 reCAPTCHA v3,用 turnstile 方法识别 Cloudflare Turnstile,识别成功率高。