深度解析

Cloudflare Turnstile 小部件模式:托管、非交互式、不可见

Cloudflare Turnstile 附带三种小部件模式,用于控制挑战的呈现方式:托管(Cloudflare 决定)、非交互式(仅工作证明,从不显示 UI)和 不可见(无小部件容器,静默运行)。该模式决定了用户看到的内容、挑战需要多长时间以及小部件是否变得可见。对于自动化,所有三种模式都会产生相同的输出 - cf-turnstile-response 令牌 - 但检测和解决它们需要了解差异。


模式对比

特征 托管 非交互式 无形的
小部件可见? 有时 从不(仅限旋转器) 绝不
需要容器元素吗? 是的 是的 是(隐藏)
需要用户交互吗? 有时(复选框)
工作量证明挑战? 是(可能会升级) 是(总是) 是(总是)
交互式复选框后备? 是的 否(反而失败) 否(反而失败)
令牌输出 cf-turnstile-response cf-turnstile-response cf-turnstile-response
CaptchaAI方法 turnstile turnstile turnstile
推荐用于 登录、注册 低摩擦形式 背景核查

托管模式(默认)

托管模式让 Cloudflare 决定每个访问者的挑战级别。大多数用户都是无形地通过的。可疑流量会看到一个复选框。高度可疑的流量可能会面临更复杂的挑战。

执行

<!-- Managed mode (default) -->
<div class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-theme="light">
</div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

自动化看到了什么

托管模式根据请求者的信号进行调整:

名声 小部件呈现为
高信任度 隐形通行证(没有可见的UI)
中等信任度 复选框小部件(点击验证)
信任度低 互动挑战或阻止

对于自动化来说,托管模式是最常见且变化最大的。根据浏览器信号,小部件可能可见也可能不可见。

HTML 中的检测

def is_managed_mode(html):
    """Check if Turnstile is using managed mode (default)."""
    # Managed mode is the default — no explicit mode attribute
    has_turnstile = "cf-turnstile" in html
    has_explicit_mode = 'data-appearance="interaction-only"' in html or \
                        'data-appearance="always"' in html or \
                        'appearance: "interaction-only"' in html
    return has_turnstile and not has_explicit_mode

非交互模式

非交互模式从不显示复选框或交互元素。它在后台运行工作证明挑战,并且仅显示加载旋转器。如果挑战无法以非交互方式完成,则挑战会失败而不是升级。

执行

<!-- Non-interactive mode -->
<div class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-appearance="interaction-only">
</div>

或者通过 JavaScript API:

turnstile.render('#turnstile-container', {
    sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
    appearance: 'interaction-only',
    callback: function(token) {
        document.getElementById('cf-turnstile-response').value = token;
    },
});

行为

Page loads → Widget initializes
    ↓
Background proof-of-work runs
    ↓
Success → Token generated (no visible UI)
    OR
Failure → Widget reports error (no fallback to checkbox)

当网站使用非交互式

  • 评论表单和反馈小部件
  • 时事通讯注册
  • 摩擦必须最小化的低价值行动
  • 具有浏览器端保护的 API 端点

隐形模式

不可见模式是真正不可见的——视口中不会出现任何容器元素。该小部件在页面加载(或编程触发)时运行,并生成一个没有任何视觉指示的令牌。

执行

<!-- Invisible mode — container is hidden -->
<div id="turnstile-invisible"
     class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-size="invisible">
</div>

或者完全通过 JavaScript:

// Programmatic invisible Turnstile
turnstile.render('#hidden-container', {
    sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
    size: 'invisible',
    callback: function(token) {
        // Token ready — submit form automatically
        submitForm(token);
    },
    'error-callback': function() {
        // Challenge failed
        console.error('Invisible Turnstile failed');
    },
});

检测挑战

隐形旋转门更难检测,因为容器没有可见的尺寸:

import re

def detect_invisible_turnstile(html):
    """Detect invisible Turnstile on a page."""
    indicators = {
        "script_loaded": "challenges.cloudflare.com/turnstile" in html,
        "size_invisible": 'data-size="invisible"' in html or
                          "size: 'invisible'" in html or
                          'size: "invisible"' in html,
        "api_render_call": "turnstile.render" in html,
        "response_field": "cf-turnstile-response" in html,
    }

    if indicators["script_loaded"] and indicators["size_invisible"]:
        return {"mode": "invisible", "confidence": "high"}
    elif indicators["script_loaded"] and indicators["api_render_call"]:
        return {"mode": "invisible_or_programmatic", "confidence": "medium"}
    elif indicators["response_field"]:
        return {"mode": "turnstile_present", "confidence": "low"}

    return {"mode": "none", "confidence": "high"}

跨所有模式提取站点密钥

无论哪种模式,解决问题都需要sitekey。从任何模式中提取它:

import re

def extract_turnstile_sitekey(html):
    """Extract Turnstile sitekey from page HTML (works for all modes)."""

    # Pattern 1: data-sitekey attribute in HTML
    match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', html)
    if match:
        return match.group(1)

    # Pattern 2: JavaScript render call
    match = re.search(r"sitekey:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
    if match:
        return match.group(1)

    # Pattern 3: Turnstile config object
    match = re.search(r"siteKey['\"]?\s*[:=]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
    if match:
        return match.group(1)

    return None

使用 CaptchaAI 解决所有三种模式

所有三种 Turnstile 模式的解决方法均与 CaptchaAI 相同。该模式不影响API调用:

Python

import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_turnstile(sitekey, page_url):
    """Solve any Turnstile mode — managed, non-interactive, or invisible."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "turnstile",
        "sitekey": sitekey,
        "pageurl": page_url,
        "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:
            return result["request"]

    raise TimeoutError("Turnstile solve timed out")


# Use with any mode
token = solve_turnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://staging.example.com/qa-login")
print(f"Token: {token[:50]}...")

Node.js

const axios = require("axios");

const API_KEY = "YOUR_API_KEY";

async function solveTurnstile(sitekey, pageUrl) {
  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: {
      key: API_KEY,
      method: "turnstile",
      sitekey,
      pageurl: pageUrl,
      json: 1,
    },
  });

  const taskId = submit.data.request;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));

    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId, json: 1 },
    });

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

  throw new Error("Turnstile solve timed out");
}

// Same function works for all Turnstile modes
solveTurnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://staging.example.com/qa-login")
  .then((token) => console.log("Token:", token.substring(0, 50)));

故障排除

症状 原因 处理方式
令牌有效,但表单拒绝它 错误的站点密钥(与可见小部件不同) 检查 JavaScript 渲染的 sitekey
在 HTML 中找不到小部件 初始渲染后加载不可见模式 等待整页加载,检查 XHR 响应
页面上有多个Turnstile小部件 不同形式的不同站点密钥 将 sitekey 与特定形式匹配
data-size="compact" 混淆检测 紧凑是一种尺寸变体,而不是一种模式 Compact默认使用托管模式
data-action 属性存在 用于分析的操作标签,而不是模式 如果需要验证,请在解决中包含操作
令牌在提交前过期 Turnstile 令牌将在 300 秒后过期 提交前解决

常见问题

Turnstile 模式会影响 CaptchaAI 的解算吗?

不会。CaptchaAI 对所有三种模式使用相同的 turnstile 方法。 sitekey 和页面 URL 是唯一必需的参数。模式不会更改令牌格式或验证流程。

我如何知道网站使用哪种模式?

检查 HTML 中的 data-appearancedata-size 属性。如果存在data-size="invisible",则为隐形模式。如果 data-appearance="interaction-only" 存在,则它是非交互式的。如果两者均未设置,则为托管模式(默认)。

站点可以动态切换模式吗?

是的。某些站点默认使用托管模式,并针对特定页面或用户段切换为非交互模式。站点密钥通常保持不变。始终重新检测导航模式。

不同模式的成功率有何差异?

CaptchaAI 在所有旋转门模式下均实现 100% 的成功率。该模式仅影响面向用户的行为——API 级别的挑战是相同的。


概括

Cloudflare Turnstile 的三种小部件模式 - 托管、非交互式和隐形 - 控制用户体验,但生成相同的 cf-turnstile-response 令牌。对于自动化,所有模式都使用相同的方式求解CaptchaAI 的旋转门求解器成功率100%。对于开发人员来说,主要区别在于检测:托管模式显示可见的 HTML,而不可见模式则需要更深入的页面分析才能找到 sitekey。

相关文章

该文章已禁用评论。