///
如何在你的网站上实现 CAPTCHA:分步指南
Vladlen Vlasov
Vladlen Vlasov
技术撰稿人
March 5, 2026
10 分钟
请查看本网站所提供内容的使用条款。

如何在你的网站上实现 CAPTCHA:分步指南

generated-image (20).png

机器人早已是你流量的一部分——不管你是否察觉。它们创建虚假账号、测试被盗密码,并在任何人高喊“事故”之前就悄悄给你的后端施加压力。理解在你的网站上如何实现 captcha不只是“可有可无”;它决定了你是把滥用当作事后才处理的问题,还是把防护直接融入你的表单、流程和基础设施之中。

本指南将带你完成两种可用于生产环境的实现:GeeTest CAPTCHA v3Cloudflare Turnstile。在此过程中,你还将了解如何获取 captcha 代码(站点密钥和凭据)、何时需要自定义 captcha、如何阻止 captcha 绕过尝试,以及如何使用 CapMonster Cloud 验证你的集成。

robots
立即开始自动化您的工作流 reCAPTCHA v2

什么是 CAPTCHA,以及为什么你的网站需要它

CAPTCHA全自动区分计算机与人类的公共图灵测试)是一种挑战-响应机制,旨在区分真实用户与自动化脚本。现代方案从传统的扭曲文本谜题,到不可见的行为分析,再到像 GeeTest 滑块这样的交互式拼图挑战,形式多样。

如果没有 CAPTCHA 这一层,你网站上的任何公开表单都会暴露在以下风险之下:

  • 凭据填充(Credential stuffing) — 使用泄露的用户名/密码组合进行自动化登录尝试,在滥用流量中很常见。
  • 垃圾提交(Spam submissions) — 机器人向联系表单、评论区或评价页面疯狂灌水。
  • 账户枚举(Account enumeration) — 脚本探测有效的邮箱地址或用户名。
  • 抓取(Scraping) — 自动化提取价格、库存或专有内容。

权衡点始终是 安全性 vs. 用户体验:阻力能挡住机器人,但阻力过大也会把真实用户推走。Cloudflare Turnstile 和 GeeTest v3 都是力求平衡这两点的现代选项:Turnstile 更偏向低打扰的验证,而 GeeTest v3 则通过不同类型的交互式拼图配合基于行为的检查来识别机器人。

如何获取你的 CAPTCHA 代码(Keys & Credentials)

在编写任何集成代码之前,你需要获取各提供商的专用凭据。

GeeTest V3

  1. 在 GeeTest 控制台创建账户。
  2. 进入 Captcha Dashboard 并选择 CAPTCHA v3:

image.png

  1. 进入可以创建新 CAPTCHA 并注册你的网站的区域(通常需要指定域名和产品类型)。
  2. 配置完成后,你会获得一个 CAPTCHA ID(通常称为 gt)以及用于服务端通信的私有 Key
  3. 安全地存储这些值(例如放在环境变量中),让应用在不硬编码密钥的情况下也能访问。

Cloudflare Turnstile

  1. 登录 dash.cloudflare.com,打开 Application security > Turnstile

image.png

  1. 点击 Add widget,输入你的域名,并选择一种组件模式: Managed、 Non-interactive 或 Invisible
  2. 如果站点走 Cloudflare Proxy(避免重复触发 CAPTCHA),将 Pre-clearance 设置为 Yes 
  3. 创建完成后,Cloudflare 会提供一个 Site Key(公开,用于客户端)和一个 Secret Key(私有,用于服务端)。
  4. 在 HTML 或 JavaScript 中使用 Site Key;在调用 Turnstile 验证 API 时,只从后端发送 Secret Key。

如何实现 GeeTest CAPTCHA v3:分步指南

GeeTest v3 采用多步骤流程:你的 服务器从 GeeTest 获取一个新的 challenge, 客户端使用该 challenge 渲染拼图(GeeTest API1),然后你的 服务器验证解题后的 tokens(GeeTest API2)。下面是一个 PHP + JavaScript 示例实现,用于展示这一模式;请根据 GeeTest 官方 SDK 和你选择的语言进行适配。

第 1 步 — 服务端注册端点

创建一个 PHP 脚本(例如 geetest_register.php),从 GeeTest 获取一个新的 challenge 并传给客户端:

<?php
header('Content-Type: application/json');
const CAPTCHA_ID = '07df3141a35**19a473d7c50';
const CAPTCHA_KEY = '543b19036ef**8e07d121b81e9';
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
function getJson($url) {
    $res = @file_get_contents($url);
    return $res ? json_decode($res, true) : null;
}
// API1:初始化
if ($path === '/register') {
    $data = getJson("https://api.geetest.com/register.php?gt=" . CAPTCHA_ID . "&json_format=1");
    echo json_encode($data ? [
        'gt' => CAPTCHA_ID,
        'challenge' => $data['challenge'],
        'success' => $data['success'] === 1,
        'new_captcha' => true
    ] : ['success' => 0]);
    exit;
}
// API2:验证
if ($path === '/validate' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $req = json_decode(file_get_contents('php://input'), true);
    $data = getJson("https://api.geetest.com/validate.php?" . http_build_query([
        'seccode' => $req['geetest_seccode'] ?? '',
        'challenge' => $req['geetest_challenge'] ?? '',
        'gt' => CAPTCHA_ID,
        'json_format' => 1
    ]));
    echo json_encode(['success' => !empty($data['seccode'])]);
    exit;
}
http_response_code(404);
echo json_encode(['error' => 'Not found']);
?>

第 2 步 — 客户端初始化

加载 GeeTest SDK,并在调用 initGeetest 时传入来自服务端(API1)的参数。以下是使用 ajax 的示例:

ajax({
    url: "https://example.com/register",
    type: "get",
    dataType: "json",
    success: function (data) {
        initGeetest({
            gt: data.gt,
            challenge: data.challenge,
            offline: !data.success,
            new_captcha: true
        }, function (captchaObj) {
            captchaObj.appendTo("#captcha");
            captchaObj.onSuccess(function () {
                const result = captchaObj.getValidate();
                ajax({
                    url: "https://example.com/validate",
                    type: "post",
                    contentType: "application/json",
                    data: JSON.stringify(result),
                    success: function(res) {
                        if (res.success) alert('CAPTCHA passed');
                        else alert('CAPTCHA failed');
                    }
                });
            });
        });
    }
});

检查运行情况

请确认:

  • /register 会返回 challenge
  • captcha 能正确显示
  • 通过 captcha 后,在浏览器控制台可以看到对 /validate 的请求
  • 服务端返回 "success": true

 

Failback(fallback 模式)

如果 GeeTest 服务器不可用:

  • 客户端会收到 success: false
  • captcha 会切换到 local mode(无需连接 GeeTest Cloud 也能工作)。要测试这一点,只需替换为错误的 CAPTCHA_ID (例如 123456789)。

Note: GeeTest 也为不同平台和语言提供官方服务端 SDK —— 你可以选择与技术栈匹配的版本。更多信息见 GeeTest CAPTCHA v3 官方文档

如何实现 Cloudflare Turnstile:分步指南

Cloudflare Turnstile 不需要单独的服务端“注册调用”。你只需在页面中嵌入一个组件,让它生成 token,然后在服务端通过 Cloudflare 的 Siteverify API 验证该 token。

第 1 步 — 引入 Turnstile 脚本

自动渲染(页面加载时自动创建组件):

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

编程式控制(通过 JavaScript 自行创建组件):

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" defer></script>

重要提示:脚本必须从精确的 URL 加载。代理或缓存可能导致失败。

第 2 步 — 为组件创建容器

自动:

<div class="cf-turnstile" data-sitekey="<YOUR_SITEKEY>"></div>

编程式:

<div id="turnstile-container"></div>

第 3 步 — 组件配置

通过 data attributes:

<div class="cf-turnstile" data-sitekey="<YOUR_SITEKEY>" data-theme="light" data-size="normal" data-callback="onSuccess"> </div>

通过 JavaScript:

const widgetId = turnstile.render("#turnstile-container", {
    sitekey: "<YOUR_SITEKEY>",
    theme: "light",
    size: "normal",
    callback: token => console.log("Token:", token)
});

第 4 步 — 使用 tokens

const token = turnstile.getResponse(widgetId); // 获取 token
const isExpired = turnstile.isExpired(widgetId); // 检查是否过期
turnstile.reset(widgetId); // 重置
turnstile.remove(widgetId); // 移除
turnstile.execute("#turnstile-container"); // 手动执行

第 5 步 — 与表单集成

<form id="my-form" method="POST"> <input type="hidden" name="cf-turnstile-response" id="cf-turnstile-response"> <button type="submit">Submit</button> </form>
<script>
    function onSuccess(token) {
      document.getElementById("cf-turnstile-response").value = token;
    }
</script>

完整代码示例

<html>

<head>
    <title>Turnstile Example</title> <!-- 引入 Turnstile 脚本 -->
    <script src="https: //challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>

<body>
    <h1>Form example with Turnstile</h1>
    <form id="my-form"> <label for="username">Name:</label> <input type="text" name="username" id="username" required> <!-- Turnstile 容器 -->
        <div class="cf-turnstile" data-sitekey="<YOUR_SITEKEY>" data-callback="onTurnstileSuccess"></div> <button type="submit">Submit</button>
    </form>
    <script>
        // 通过 CAPTCHA 后触发的回调
        function onTurnstileSuccess(token) {
          console.log("Received Turnstile token:", token);
          // 将 token 保存到隐藏表单字段(可选)
          document.getElementById("cf-turnstile-token")?.remove();
          const input = document.createElement("input");
          input.type = "hidden";
          input.name = "cf-turnstile-response";
          input.id = "cf-turnstile-token";
          input.value = token;
          document.getElementById("my-form").appendChild(input);
        }
        // 表单提交
        document.getElementById("my-form").addEventListener("submit", async (e) => {
          e.preventDefault();
          const formData = new FormData(e.target);
          const response = await fetch("/submit-form", {
            method: "POST",
            body: formData
          });
          const result = await response.json();
          if(result.success){
            alert("Form successfully submitted and token verified!");
          } else {
            alert("Turnstile token verification error. Please try again.");
            // 重置组件,让用户可以再次完成 CAPTCHA
            turnstile.reset();
          }
        });
    </script>
</body>

</html>

第 6 步 — 配置服务端部分

服务端验证流程:

  • 客户端:用户在页面上完成 Turnstile → 生成 token。
  • 提交表单:token 会与表单数据一起发送到服务器。
  • 服务器:携带 token 和 secret 向 Cloudflare Siteverify API 发起 POST 请求。
  • Cloudflare:返回包含结果(success: true/false)及附加信息(action、hostname、completion time)的 JSON。
  • 服务器:决定允许还是拒绝用户操作。

Siteverify API:

POST
https://challenges.cloudflare.com/turnstile/v0/siteverify

请求参数:

  • secret (required):Cloudflare 面板中的 Turnstile secret key
  • response (required):客户端收到的 token
  • remoteip (optional):用户的 IP 地址(推荐)
  • idempotency_key (optional):用于防止重复验证的唯一 UUID

Token 属性:

  • 最大长度:2048 字符
  • 有效期 5 分钟
  • 单次使用
  • 过期或重复验证时,API 将返回 timeout-or-duplicate 错误

PHP 验证示例:

<?php
function validateTurnstile($token, $secret, $remoteip = null) {
    $url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
    $data = ['secret' => $secret,
        'response' => $token];
    if ($remoteip) $data['remoteip'] = $remoteip;
    $options = [
        'http' => [
            'header' => "Content-type: application/x-www-form-urlencoded
            ",
            'method' => 'POST',
            'content' => http_build_query($data)
        ]
    ];
    $response = file_get_contents($url, false, stream_context_create($options));
    if ($response === FALSE) {
        return ['success' => false,
            'error-codes' => ['internal-error']];
    }
    return json_decode($response, true);
}
// 用法
$secret_key = 'YOUR_SECRET_KEY';
$token = $_POST['cf-turnstile-response'] ?? '';
$remoteip = $_SERVER['REMOTE_ADDR'];
$result = validateTurnstile($token, $secret_key, $remoteip);
if ($result['success']) {
    echo "Form successfully submitted!";
} else {
    echo "Verification error: " . implode(', ', $result['error-codes']);
}
?>

Note: 关于安装与配置 Cloudflare Turnstile 的全部详细说明(包括客户端与服务端集成、代码示例、错误说明与安全建议),请参阅 Cloudflare 官方文档

自定义 CAPTCHA:何时以及为什么

自定义 captcha 是指你自行构建的挑战-响应系统,而不是集成第三方服务。常见的轻量方案包括:

  • 数学题 — 例如“\(4 + 9\) 等于多少?”以纯文本或简单生成图片的形式呈现。
  • Honeypot 字段 — 隐藏表单字段,通常只有机器人会去填写。
  • 基于时间的检查 — 拒绝“以人类不可能的速度”完成的提交。
  • 自定义图片挑战 — 使用你自己的素材做“点击不属于该组的元素”等题目。

当你需要严格的品牌控制、当严格的隐私要求限制第三方脚本、或当你的风险较低且只需要基本的机器人阻力时,自定义 captcha 可能会有用。不过,它们通常缺乏 GeeTest 或 Turnstile 这类服务的复杂行为模型,也更容易被高级机器人攻破。

你还需要对 无障碍(例如提供音频替代方案)以及随着机器人技术演进而持续的 维护承担全部责任。对于登录、结账、重置密码等关键流程,托管方案通常比完全自定义 captcha 更安全。

如何更有效地拦截 CAPTCHA 机器人

实现 CAPTCHA 是必要的,但单靠它往往并不够;有决心的攻击者会尝试绕过或自动化它。要更有效地阻止 captcha 机器人,请叠加多层防护。

第 1 层 — 基础设施层面的限流

在 Web 服务器或 CDN 上进行限流,即使机器人能解题或绕过 captcha,也会更难对表单进行暴力尝试。例如,在 Nginx 中:

# Nginx:将登录端点限制为每个 IP 每秒 10 个请求
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/s;
location /login {
    limit_req zone=login burst=5 nodelay;
    # proxy_pass ...
}

第 2 层 — Honeypot 字段

一个真实用户永远看不到、但不够“聪明”的机器人可能会填写的隐藏字段,是一种低打扰的防御方式。

<div aria-hidden="true">
  <input type="text" name="website" tabindex="-1" autocomplete="off">
</div>

在服务端,拒绝任何 website 非空的提交。这样几乎不会增加 UX 成本,同时能过滤掉一部分自动化提交。

第 3 层 — 其他防护层

  • IP 信誉过滤:在边缘侧封禁已知的数据中心网段或匿名代理服务,可显著减少滥用流量。
  • 防止 token 重放:Turnstile token 必须在服务端验证并按单次使用处理;丢弃被重复使用的 token 有助于防止重放攻击。
  • 设备或会话指纹:外部工具可帮助跨 IP 与会话关联可疑模式,但会带来隐私方面的考量。
  • 监控与告警:跟踪验证失败率、特定地区或 ASN 流量的突然激增,把它们作为攻击进行中的信号。
robots
立即开始自动化您的工作流 reCAPTCHA v2

使用 CapMonster Cloud 测试你的 CAPTCHA 集成

当你的 CAPTCHA 上线后,你需要一种可靠的方法,在自动化测试流水线中覆盖完整的请求-响应闭环——而不必在每次 CI 运行时手动解谜。 CapMonster Cloud 是一个基于 API 的 CAPTCHA 求解服务,支持 GeeTest v3 和 Cloudflare Turnstile 等多种类型。你提交一个任务来描述需要被求解的 captcha,API 会返回解决方案 token,你的测试可以将其直接注入到表单提交中。

两种 CAPTCHA 类型的完整流程都由相同的三个步骤组成: 创建任务 → 轮询结果 → 使用解决方案

GeeTest V3 — CapMonster Cloud 完整流程

第 1 步 — 创建任务

从你的 GeeTest 注册端点获取一个新的 challenge 后,立刻调用 createTask。 challenge 值的有效期很短且只能使用一次。

请求:

POST https://api.capmonster.cloud/createTask

{
  "clientKey": "YOUR_CAPMONSTER_API_KEY",
  "task": {
    "type":"GeeTestTask",
    "websiteURL": "https://example.com/your-page",
    "gt":"022397c99c9f646f6477822485f30404",
    "challenge":"7f044f48bc951ecfbfc03842b5e1fe59",
    "geetestApiServerSubdomain":"api-na.geetest.com"
  }
}

响应:

{
  "errorId": 0,
  "taskId": 407533072
}

第 2 步 — 轮询结果

循环调用 getTaskResult,每次轮询之间等待几秒。GeeTest V3 任务通常会在 10–30 秒内完成,具体取决于系统负载。

请求:

POST https://api.capmonster.cloud/getTaskResult

{
  "clientKey": "YOUR_CAPMONSTER_API_KEY",
  "taskId": 407533072
}

解题进行中时的响应:

{
  "errorId":0,
  "status":"processing"
}

就绪时的响应:

{
  "errorId":0,
  "status":"ready",
  "solution":{
    "challenge":"0f759dd1ea6c4wc76cedc2991039ca4f23",
    "validate":"6275e26419211d1f526e674d97110e15",
    "seccode":"510cd9735583edcb158601067195a5eb|jordan"
  }
}

 solution 中的三个值—— challenge、 validate 和 seccode——必须一起提交给你的后端,完全按照真实浏览器发送它们的方式提交。

第 3 步 — 提交解决方案

将三个解决方案值 POST 到你的表单端点,并使用你的 GeeTest 集成所期望的相同请求体字段:

const result = await pollForResult(taskId);
    await fetch('/submit_form.php', {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        geetest_challenge: result.solution.challenge,
        geetest_validate:  result.solution.validate,
        geetest_seccode:   result.solution.seccode,
      })
    });

Cloudflare Turnstile — CapMonster Cloud 完整流程

第 1 步 — 创建任务

请求:

POST https://api.capmonster.cloud/createTask

{
  "clientKey": "API_KEY",
  "task": {
   "type": "TurnstileTask",
   "websiteURL": "http://tsmanaged.zlsupport.com",
   "websiteKey": "0x4AAAAAAABUYP0XeMJF0xoy"
  }
}

关于参数说明的详细信息,请参阅 CapMonster 文档

响应:

{
  "errorId": 0,
  "taskId": 407533072
}

第 2 步 — 轮询结果

Turnstile 任务通常会在 5–20 秒内完成。

请求:

POST https://api.capmonster.cloud/getTaskResult

{
  "clientKey": "YOUR_CAPMONSTER_API_KEY",
  "taskId": 407533072
}

解题进行中时的响应:

{
  "errorId": 0,
  "status": "processing"
}

就绪时的响应:

{
  "errorId": 0,
  "status": "ready",
  "solution": {
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
    "token": "0.iGX3xsyFCkbGePM3jP4P4khLo6TrLukt8ZzBvwuQOvbC...f61f3082"
  }
}

solution.token 是在提交表单时填入 cf-turnstile-response 的值。在你的 HTTP 客户端或 Playwright/Selenium 实例中使用 solution.userAgent,以匹配 token 被求解时所处的环境——环境不一致可能导致服务端拒绝。

第 3 步 — 提交解决方案

在提交表单之前注入已求解的 token——可以直接写入隐藏 input,或通过 Cloudflare 的 JavaScript 回调注入:

// 选项 A:直接向 DOM 注入(用于 Playwright/Selenium 测试)
    await page.evaluate(token => {
      const field = document.querySelector('[name="cf-turnstile-response"]');
      if (field) field.value = token;
    }, result.solution.token);
// 选项 B:直接触发 Turnstile 回调(用于 SPA 测试)
    await page.evaluate(token => {
      if (window.turnstileCallback) window.turnstileCallback(token);
    }, result.solution.token);
    await page.click('#submit-btn');

CapMonster Cloud 错误处理

 createTask 和 getTaskResult 使用相同的错误封装格式。 errorId 为 0 始终表示成功; errorId: 1 表示出现问题,具体的错误类型会在字符串字段 errorCode 中返回。关于可能出现的错误完整列表,请参阅 CapMonster 文档

错误响应示例:

{
  "errorId": 1,
  "errorCode": "ERROR_KEY_DOES_NOT_EXIST"
}

最佳实践与提示

优雅处理 Token 过期

GeeTest 的 challenge 和 Turnstile 的 token 都有时间限制,因此如果用户在提交表单前等待太久,你应预期会偶尔过期。实现 Turnstile 的 expired-callback 等回调,或重新获取一个新的 GeeTest challenge 并重置组件,而不是静默失败或抛出通用错误。

无障碍(WCAG 2.1)

  • Cloudflare Turnstile 提供视觉挑战和替代挑战流程,旨在与辅助技术配合使用,但你应该使用用户依赖的屏幕阅读器和浏览器进行测试。

  • GeeTest v3 的滑块拼图以鼠标/触控为主,这可能会给某些存在运动或协调障碍的用户带来挑战;在需要强无障碍支持的场景中,考虑提供替代验证路径。

  • 避免在面向残障用户的关键流程中把 CAPTCHA 作为唯一保护机制;应将其与限流、异常检测等其他防护结合使用。

隐私与第三方数据

使用 Turnstile 或 GeeTest 意味着将请求与交互数据发送给第三方提供商进行分析与验证。你的隐私政策与同意机制应如实反映这一点,尤其是在 GDPR 或 CCPA 等框架下,此类处理可能被视为个人数据处理。

策略性地应用 CAPTCHA —— 不要到处都用

表单 / 端点

建议

登录

使用 CAPTCHA,尤其是在多次失败尝试之后。

注册

使用 CAPTCHA 以减少虚假账户创建。

重置密码

使用 CAPTCHA,因为这些流程容易被滥用。

联系 / 评论表单

使用 CAPTCHA,尤其是在对公众开放的端点上。

搜索

优先考虑限流,仅在反复滥用后才加入 CAPTCHA。

公开产品页面

通常不需要,除非抓取是重大隐患。

区分测试与生产环境的 Key

Cloudflare 和大多数 CAPTCHA 提供商都建议为开发、预发布(staging)和生产使用不同的 key 或环境。请将你的 Secret Key 以及任何等价的私有 key 存放在环境变量或专用的密钥管理器中,而不是硬编码到代码里。

保持 SDK 与集成更新

CAPTCHA 服务会随着时间推移更新其检测逻辑与 API,以应对新的攻击技术。定期查看提供商的变更日志或文档并更新你的集成,有助于避免使用已弃用的端点或落入被弱化的配置。

结语

正确理解并掌握 如何实现 CAPTCHA,不只是把一个组件塞进 HTML 这么简单;更重要的是,将稳健的客户端流程与可靠的服务端验证配对,并把二者纳入更广泛的防滥用策略中。GeeTest v3 提供基于行为的拼图挑战,可以提高机器人操作者的门槛;而 Cloudflare Turnstile 能自然融入 Cloudflare 驱动的网站,并强调低打扰验证。

无论你选择哪种方案,将 CAPTCHA 与限流、honeypot 字段和 IP 信誉过滤结合使用,都会显著提高滥用表单所需的成本。而当你需要测试集成或自动化复杂工作流时, CapMonster Cloud 通过一个简单的 API,为你提供了可编程的方式来求解 GeeTest 与 Cloudflare Turnstile 挑战——让你更容易在应用演进的同时持续确保防护有效。

准备把自动化 CAPTCHA 测试加入你的工具箱了吗? 探索 CapMonster Cloud,并立即开始把他们的 GeeTest 与 Turnstile 任务类型集成到你的开发与 QA 工作流中。

robots
立即开始自动化您的工作流 reCAPTCHA v2

NB: 请注意,本产品仅用于对您自身的网站以及您依法拥有访问权限的资源进行自动化测试
ItGuy
geear
面向软件开发者的联盟计划
通过用户识别验证码的消费获取高达 30% 的返佣。
✅ 请求已发送
感谢您对我们的合作伙伴计划感兴趣!我们将在 7 个工作日内与您联系。
请求加入
填写表格以提交申请加入合作伙伴计划。
更多文章