# waf.bi 人机验证对接说明 完全免费,任何网站都能用,用法与 Cloudflare Turnstile 一致。 只在你调用的地方生效,不影响 App 和对外 API。 ## 三步接入 1. 在 waf.bi 控制台「人机验证」创建密钥,得到 Site Key(前端用)和 Secret(后端用)。 2. 前端引入脚本并放置挂件,访客通过后拿到 token,随请求提交。 3. 后端用 Secret 调用核验接口,success 为 true 才放行。 验证服务地址:https://captcha.waf.bi/__captcha/ ## 前端:普通表单 ```html
``` ## 前端:前后端分离 ```html
``` ### 挂件选项 | JS 选项 | HTML 属性 | 说明 | |---|---|---| | sitekey | data-sitekey | 必填,密钥的 Site Key | | callback | data-callback | 验证通过时调用,参数为 token | | expired-callback | data-expired-callback | token 过期时调用(约 5 分钟) | | error-callback | data-error-callback | 验证失败时调用,参数为错误码 | | response-field-name | data-response-field-name | 表单隐藏字段名,默认 wafbi-captcha-response | ### JS 方法 | 方法 | 说明 | |---|---| | wafbiCaptcha.render(容器, 选项) | 手动渲染挂件,返回挂件 ID。容器可以是元素或 CSS 选择器 | | wafbiCaptcha.getResponse(ID) | 取当前 token,未通过时为空字符串。省略 ID 取第一个挂件 | | wafbiCaptcha.reset(ID) | 重置挂件,访客需重新验证。每次提交后调用 | | wafbiCaptcha.remove(ID) | 移除挂件 | ## 后端核验 POST https://captcha.waf.bi/__captcha/siteverify Content-Type: application/x-www-form-urlencoded 或 application/json | 参数 | 是否必填 | 说明 | |---|---|---| | secret | 必填 | 密钥的 Secret,只能放在服务端 | | response | 必填 | 前端拿到的 token(表单字段 wafbi-captcha-response) | | remoteip | 可选 | 访客 IP,传了会与完成验证的 IP 比对,不一致返回失败 | 返回: ```json { "success": true, "error-codes": [], "hostname": "www.example.com", "challenge_ts": "2026-10-25 14:30:00" } { "success": false, "error-codes": ["invalid-input-response"] } ``` hostname 是嵌入挂件的页面域名,challenge_ts 是完成验证的时间(北京时间)。 | error-codes | 含义 | |---|---| | missing-input-secret | 没有传 secret | | invalid-input-secret | secret 错误、已重置,或密钥已停用、删除 | | missing-input-response | 没有传 token | | invalid-input-response | token 无效、已过期(约 5 分钟)或已经核验过一次 | | remoteip-mismatch | 传入的 remoteip 与完成验证的访客 IP 不一致 | | bad-request | 请求格式错误 | ### Go ```go // secret 放在服务端配置里,例如环境变量 WAFBI_CAPTCHA_SECRET var captchaClient = &http.Client{Timeout: 5 * time.Second} func verifyCaptcha(ctx context.Context, token, clientIP string) (bool, error) { form := url.Values{ "secret": {os.Getenv("WAFBI_CAPTCHA_SECRET")}, "response": {token}, "remoteip": {clientIP}, // 可选,传了会比对 } req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://captcha.waf.bi/__captcha/siteverify", strings.NewReader(form.Encode())) if err != nil { return false, err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, err := captchaClient.Do(req) if err != nil { return false, err } defer resp.Body.Close() var out struct { Success bool `json:"success"` } if err := json.NewDecoder(io.LimitReader(resp.Body, 4096)).Decode(&out); err != nil { return false, err } return out.Success, nil } ``` ### Node.js ```javascript // Node.js 18+ async function verifyCaptcha(token, clientIP) { const res = await fetch('https://captcha.waf.bi/__captcha/siteverify', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ secret: process.env.WAFBI_CAPTCHA_SECRET, response: token, remoteip: clientIP, // 可选 }), signal: AbortSignal.timeout(5000), }) const data = await res.json() return data.success === true } ``` ### PHP ```php true, CURLOPT_POSTFIELDS => http_build_query([ 'secret' => getenv('WAFBI_CAPTCHA_SECRET'), 'response' => $token, 'remoteip' => $clientIP, ]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, ]); $body = curl_exec($ch); curl_close($ch); $data = json_decode($body ?: '{}', true); return ($data['success'] ?? false) === true; } // 表单提交:verify_captcha($_POST['wafbi-captcha-response'] ?? '', $_SERVER['REMOTE_ADDR']) ``` ### Python ```python import os import requests def verify_captcha(token: str, client_ip: str = "") -> bool: resp = requests.post( "https://captcha.waf.bi/__captcha/siteverify", data={ "secret": os.environ["WAFBI_CAPTCHA_SECRET"], "response": token, "remoteip": client_ip, # 可选 }, timeout=5, ) return resp.json().get("success") is True ``` ## 从 Cloudflare Turnstile 迁移 | 项目 | Turnstile | waf.bi | |---|---|---| | 脚本地址 | https://challenges.cloudflare.com/turnstile/v0/api.js | https://captcha.waf.bi/__captcha/api.js | | 容器 class | cf-turnstile | wafbi-captcha | | 表单字段 | cf-turnstile-response | wafbi-captcha-response | | 全局对象 | turnstile | wafbiCaptcha | | 核验地址 | https://challenges.cloudflare.com/turnstile/v0/siteverify | https://captcha.waf.bi/__captcha/siteverify | ## 注意事项 - 一定要在后端核验,Secret 不能写进前端代码或 App。 - token 只能核验一次,约 5 分钟过期。提交后调用 wafbiCaptcha.reset()。 - 核验请求设置 5 秒左右超时。