脚本加了随机延时、模拟了鼠标轨迹,reCAPTCHA 分数还是 0.1,图片验证照样弹出来——这不是运气问题。reCAPTCHA 从加载的第一毫秒就开始采集特征,等脚本点到复选框时,判断早就做完了:JS 环境探测、Canvas/WebGL 指纹、行为分析、网络与 TLS 特征、跨会话情报,共五层。本文逐层拆解 reCAPTCHA 到底在看什么,以及 CaptchaAI 这类 API 求解器为什么能稳定拿到 token。
检测层 1:JavaScript 环境探测
reCAPTCHA 会跑一批 JavaScript 探针,专找无头浏览器和自动化框架的破绽。
navigator.userAgent:最容易暴露的信号
自动化框架启动时常留下一个可读属性,reCAPTCHA 第一时间就查这个值:
// Selenium/Puppeteer set this automatically
navigator.userAgent === true // → Automation detected
// Real browser
navigator.userAgent === undefined // or false → Normal browser
一旦命中,reCAPTCHA 立即把会话标记为自动化,分数通常直接掉到 0.1 或更低,后面的图片验证基本都是从这一步开始的。
缺失的浏览器 API:headless 环境补不齐的细节
reCAPTCHA 还会成批检查 headless 浏览器常常缺失或实现不一致的 API:
// Probes reCAPTCHA performs (simplified)
const checks = {
// Chrome-specific object
hasChrome: !!window.chrome,
hasChromeRuntime: !!(window.chrome && window.chrome.runtime),
// Plugin and MIME type arrays
pluginCount: navigator.plugins.length,
mimeTypeCount: navigator.mimeTypes.length,
// Notification permission
notificationPermission: Notification.permission,
// Speech synthesis voices
speechVoices: window.speechSynthesis.getVoices().length,
// Performance observer
hasPerformanceObserver: typeof PerformanceObserver !== "undefined",
};
真实 Chrome 和 headless 模式的差异很典型:
| 探测项 | 真实 Chrome(预期) | headless 模式 | 判定 |
|---|---|---|---|
window.chrome |
对象 | 未定义或内容极简 | 自动化 |
navigator.plugins |
2–5 个插件 | 空数组 | 自动化 |
navigator.permissions |
带 query() 的对象 | 可能抛错或缺失 | 自动化 |
Notification.permission |
"default" |
可能抛错 | 自动化 |
window.speechSynthesis |
有语音列表的对象 | 空或缺失 | 自动化 |
原型链篡改检测
更精细的方案会重写浏览器 API 来掩盖痕迹,reCAPTCHA 对此也有对应手段:
// reCAPTCHA may check if native functions were modified
const nativeToString = Function.prototype.toString;
const pluginsToString = navigator.plugins.toString();
// Overridden functions have different toString output:
// Native: "function get plugins() { [native code] }"
// Overridden: "function () { return [...fakePlugins] }"
原生函数和被重写函数的 toString() 输出不一样,reCAPTCHA 靠这个差异分辨 API 是不是被动过手脚。
检测层 2:Canvas 与 WebGL 指纹
Canvas 指纹采集
reCAPTCHA 在隐藏 canvas 上画文字和图形,再把像素数据读回来。操作系统、GPU、字体渲染引擎、抗锯齿设置任何一项不同,结果都不一样:
// Simplified canvas fingerprint
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx.textBaseline = "alphabetic";
ctx.font = "14px Arial";
ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069";
ctx.fillText("CaptchaTest,!", 2, 15);
const fingerprint = canvas.toDataURL();
// Unique per browser/OS/GPU combination
典型判定信号:
| 观察结果 | 判定 |
|---|---|
| 不同系统报出完全相同的指纹 | 大概率是伪造的 |
| canvas 读回的数据统一或空白 | 很可能是 headless 环境 |
| 指纹命中已知的 headless Chrome 特征库 | 直接标记为自动化 |
WebGL 指纹:SwiftShader 就是虚拟机的实锤
const gl = document.createElement("canvas").getContext("webgl");
const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
const vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
// Real browser: "ANGLE (NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0)"
// Headless Chrome: "Google Inc. (Google SwiftShader)" ← Strong bot signal
SwiftShader 是 Google 在没有物理 GPU 时用的软件渲染器——多数云服务器、CI 环境跑出来的正是这个名字,对 reCAPTCHA 来说是清晰的无头环境信号。
检测层 3:行为分析
前两层看环境,这一层看操作,覆盖三个维度:
- 鼠标的速度、轨迹与微抖动
- 键盘的间隔、节奏与出错率
- 加载到操作的时间线
鼠标轨迹分析
reCAPTCHA records:
├─ Mouse coordinates at ~60fps intervals
├─ Velocity and acceleration at each point
├─ Trajectories between clickable elements
├─ Hover patterns over links and buttons
├─ Micro-movements while "stationary"
└─ Natural overshoot when targeting elements
Human pattern:
- Curved paths with variable speed
- Natural acceleration/deceleration (Fitts's Law)
- Random micro-jitter during hovering
- Occasional overshoot and correction
Bot pattern:
- Zero mouse events (no mouse simulation)
- Straight lines at constant speed
- Perfect targeting (no overshoot)
- Identical patterns across sessions
键盘输入节奏分析
reCAPTCHA records:
├─ Inter-key interval for each key pair
├─ Key hold duration (keydown to keyup)
├─ Error rate (backspace frequency)
├─ Typing rhythm consistency
└─ Input method (keyboard vs paste vs JavaScript)
Human pattern:
- Variable intervals (80-300ms typical)
- Faster for common character pairs
- Occasional errors and corrections
- keydown → keypress → keyup sequence
Bot pattern:
- Constant intervals or instant input
- No keypress events (value set via JS)
- Zero errors
- All characters appear simultaneously
时序与交互顺序分析
reCAPTCHA records:
├─ Time from page load to first interaction
├─ Time from CAPTCHA rendering to click
├─ Scroll events and depths
├─ Focus/blur events on form fields
└─ Tab between fields vs click between fields
Suspicious patterns:
- First interaction < 1 second after page load
- CAPTCHA clicked immediately after rendering
- No scroll events before interacting with below-fold content
- All form fields filled in <500ms
举个例子:某跨境电商团队跑 QA 回归测试,脚本直接给字段赋值、0.3 秒填完整页——纯功能测试没问题,但页面挂着 reCAPTCHA 时几乎每次都会命中时序异常规则。
检测层 4:网络与 IP 特征分析
IP 信誉库
Google 维护着完整的 IP 情报库:
| 维度 | 说明 |
|---|---|
| 已知数据中心网段 | AWS(52.x.x.x、54.x.x.x 等)、GCP、Azure、DigitalOcean 等云厂商 |
| 已知代理 / VPN 服务商 | NordVPN、ExpressVPN 以及各类商业代理 |
| Tor 出口节点 | 公开列表,定期更新 |
| 滥用记录 | 涉及垃圾请求、抓取或验证码刷量的历史 IP |
| 地理位置跳变 | 短时间内跨地区切换的 IP 会被视为代理特征 |
TLS 握手指纹(JA3/JA4)
每种 HTTP 客户端的 TLS 握手参数组合都是唯一的:
Chrome 120: JA3 = 771,4865-4866-4867-49195-49199-49196..
Python/requests: JA3 = 771,4866-4867-4865-49196-49200..
curl/libcurl: JA3 = 771,49196-49200-159-52393-52392..
reCAPTCHA 会核对 TLS 指纹和 User-Agent 是否一致——用 Python requests 把 User-Agent 伪装成 Chrome,握手参数却还是 Python 默认组合,这种不匹配很容易被识别。
HTTP 请求头一致性检查
Real Chrome headers:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,...
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Sec-CH-UA: "Not_A Brand";v="8", "Chromium";v="120"
Sec-CH-UA-Platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Automation headers (missing or different):
- Missing Sec-CH-UA headers
- Missing Accept-Language
- Non-standard Accept header
- Missing Sec-Fetch-* headers
检测层 5:跨会话情报关联
reCAPTCHA 会把多个会话、多个站点的信号串起来看:
| 关联维度 | 说明 |
|---|---|
| 指纹关联 | 同一套浏览器指纹在很多站点高频发起请求 |
| 求解节奏 | 用时落在异常固定的区间(真人耗时本来就有波动) |
| 挑战响应关联 | 多个会话在几秒内解出同一道题 |
| Cookie 时间线 | 同一 IP 反复出现没有历史 Cookie 的"全新会话" |
API 求解器如何应对这五层检测
CaptchaAI 这类 API 求解器思路很直接:让自动化代码完全不接触 reCAPTCHA,挑战在独立、优化过的环境里完成:
Your automation:
Extracts sitekey + pageurl from target page
↓
Sends to CaptchaAI API (HTTPS request to ocr.captchaai.com)
↓
CaptchaAI's solver environment:
├─ Real browser with genuine fingerprint (not headless)
├─ Human-like behavioral patterns
├─ Clean residential IP
├─ Valid cookies and session history
├─ Matching TLS/header fingerprints
└─ Solves the challenge with human-like behavior
↓
Returns valid g-recaptcha-response token
↓
Your automation:
Submits token to target website
↓
Target website validates token with Google
→ Google sees a legitimate solve from a trusted environment
→ Token validated: success = true
核心逻辑:
- 自动化代码从始至终不直接和 reCAPTCHA 打交道
- 特征采集、行为分析、挑战本身都在求解器那一侧完成
- 你的代码只需要提交最后拿到的 token
Python 示例
import requests
import time
API_KEY = "YOUR_API_KEY"
# Your automation only needs sitekey and pageurl
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": "6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp",
"pageurl": "https://staging.example.com/qa-login",
"json": 1,
})
task_id = submit.json()["request"]
# Poll for token
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"]
# Submit this token to the target site's form
print("Token received — submit to target form")
break
Node.js 示例
const axios = require("axios");
async function solveRecaptcha(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,
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");
}
常见问题
reCAPTCHA 分数从 0.9 掉到 0.1,是哪一层检测触发的?
大概率不是单一原因,排查顺序:
- 环境指纹像不像 headless(零配置脚本最常见)
- 行为轨迹够不够"像人"(太规整、太快,最常见)
- IP 和历史会话信誉是否有前科
国内网络环境访问 reCAPTCHA 较慢,会拉低分数吗?
reCAPTCHA 依赖 Google 托管脚本,国内网络访问不稳定是常见现象。加载慢本身不直接扣分,但若因此导致超时重试、行为异常,间接触发的还是行为分析规则。API 方式识别不受此影响。
Cloudflare Turnstile 和 reCAPTCHA 的检测逻辑一样吗?
思路接近但不完全一样:两者都做环境指纹和行为分析,但 Turnstile 更依赖 Cloudflare 边缘网络信号,多数情况不需要用户交互就能通过;reCAPTCHA v2 更常见的是弹出图片挑战。两者 API 调用方式各自独立,参数不能混用。
CaptchaAI 返回的 token 会被 reCAPTCHA 标记为异常吗?
不会。reCAPTCHA 只按自己的规则校验 token 本身——只要求解环境产出的是带有真实类人行为的合法求解,token 就是有效的。Google 看到的是一次正常的人类通过,token 本身不携带生成方式的额外信息。
reCAPTCHA 的检测规则多久更新一次?
节奏大致是这样:
- 大版本更新:大约每隔几个月一次
- 新自动化工具的专项检测:往往几周内就跟进上线
- CaptchaAI 这类维护自有求解环境的服务:能第一时间跟着调整,不需要你自己盯着规则更新
小结
reCAPTCHA 通过五层信号判断自动化:JS 环境探测、Canvas/WebGL 指纹、行为分析、网络与 IP 信誉、跨会话情报关联。基于 API 的求解器,例如CaptchaAI,在独立、优化过的环境里完成这五层检测并返回有效 token,自动化代码全程不需要直接面对 reCAPTCHA。
相关文章
- 如何用 API 解决 reCAPTCHA v2 回调验证码
- reCAPTCHA v2 与 Turnstile 同站点处理指南
- reCAPTCHA Enterprise Assessment API 深度解析