Explainers

reCAPTCHA 如何检测自动化以及 API 求解器如何工作

脚本加了随机延时、模拟了鼠标轨迹,reCAPTCHA 分数还是 0.1,图片验证照样弹出来——这不是运气问题。reCAPTCHA 从加载的第一毫秒就开始采集特征,等脚本点到复选框时,判断早就做完了:JS 环境探测、Canvas/WebGL 指纹、行为分析、网络与 TLS 特征、跨会话情报,共五层。本文逐层拆解 reCAPTCHA 到底在看什么,以及 CaptchaAI 这类 API 求解器为什么能稳定拿到 token。


检测层 1:JavaScript 环境探测

reCAPTCHA 会跑一批 JavaScript 探针,专找无头浏览器和自动化框架的破绽。

自动化框架启动时常留下一个可读属性,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。

相关文章

该文章已禁用评论。