Guard 接入指南
Sad Guard 是由伤心的云运营的人机验证服务。本文档说明如何将它接入你的网站或应用。
概览
一次完整的验证流程分三步:
浏览器(你的页面) Guard 服务器 你的后端
│ │ │
│── POST /{siteKey}/handshake ──▶│ │
│◀── 挑战数据 + ticket ──────────│ │
│ │ │
│ [用户在页面上完成验证] │ │
│── POST /{siteKey}/confirm ────▶│ │
│◀── 一次性 pass token ──────────│ │
│ │ │
│── 把 pass token 随表单 ───────│────────────────────────▶│
│ │ │── POST /siteverify ──▶│
│ │ │◀── {success:true} ────│
│ │ │
│◀──────────── 业务响应(登录/注册/发送验证码等)──────────│
Guard 不参与你的业务逻辑——它只负责发放和校验一次性 pass token。
第一步:获取密钥
- 登录 Guard 管理面板(默认地址
/admin)。 - 进入 Site Keys,点击创建。
- 你会得到两个值:
- Site Key(公开):前端 widget 使用。
- Secret Key(保密):后端调用
/siteverify时使用。不要放到前端代码里。
第二步:前端接入
在你的 HTML 页面里加入两行代码:
<!-- 1. 引入组件脚本(一次) -->
<script src="https://guard.sad.kim/guard.js" defer></script>
<!-- 2. 放置验证组件(在表单里) -->
<guard-widget
site-key="YOUR_SITE_KEY"
api="https://guard.sad.kim">
</guard-widget>
验证完成后的事件监听
widget 在验证通过时会派发 solved 事件,携带一次性 pass token:
<script>
const widget = document.querySelector('guard-widget');
widget.addEventListener('solved', (e) => {
const passToken = e.detail.token;
// 把 passToken 随表单一起提交给你的后端
document.getElementById('guardToken').value = passToken;
});
widget.addEventListener('error', (e) => {
console.error('验证失败', e.detail.error);
});
</script>
隐藏表单字段
widget 内部会自动渲染一个 <input type="hidden" name="guard-token">。如果你的表单名不同,用 field-name 属性覆盖:
<guard-widget
site-key="YOUR_SITE_KEY"
api="https://guard.sad.kim"
field-name="captcha_token">
</guard-widget>
Widget 属性一览
| 属性 | 必填 | 默认值 | 说明 |
|---|---|---|---|
site-key |
✅ | — | 管理面板里的 Site Key |
api |
✅ | — | Guard 服务器地址(不带尾部 /) |
field-name |
❌ | guard-token |
隐藏 input 的 name |
mode |
❌ | 服务端配置 | 强制使用某种验证模式(pow / pow_slider / pow_click) |
编程式控制
widget 暴露两个方法,可在 JS 中直接调用:
const w = document.querySelector('guard-widget');
w.solve(); // 主动发起验证(需先完成上一次的 reset)
w.reset(); // 重置状态,可再次验证
第三步:后端验证
用户提交表单后,你的后端会拿到 pass token。必须调 Guard 的 /siteverify 接口核销——否则攻击者可以伪造任意 token 绕过验证。
请求
POST https://guard.sad.kim/siteverify
Content-Type: application/json
{
"secret": "YOUR_SECRET_KEY",
"response": "<用户提交的 pass token>"
}
响应
// 成功
{ "success": true }
// 失败
{ "success": false, "error": "invalid_or_expired_token" }
关键约束
- token 一次有效:核销后立即失效,重放攻击无法通过。
- token 有效期 2 小时:超时的 token 会被拒。
- secret 与 site-key 必须匹配:服务端通过 secret 反查 site-key,不一致则失败。
Python 示例
import requests
def verify_captcha(pass_token: str) -> bool:
resp = requests.post(
"https://guard.sad.kim/siteverify",
json={
"secret": "YOUR_SECRET_KEY",
"response": pass_token,
},
timeout=5,
)
data = resp.json()
return data.get("success") is True
Node.js 示例
async function verifyCaptcha(passToken) {
const resp = await fetch('https://guard.sad.kim/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
secret: 'YOUR_SECRET_KEY',
response: passToken,
}),
});
const data = await resp.json();
return data.success === true;
}
验证模式
Guard 支持三种验证强度,可在管理面板为每个 Site Key 单独配置:
| 模式 | 用户体验 | 适用场景 |
|---|---|---|
pow |
无交互,后台计算约 1-3 秒 | 低风险接口(搜索、浏览) |
pow_slider |
拖动滑块拼图 | 登录、注册 |
pow_click |
按顺序点击图中文字 | 高价值操作(支付、改密) |
所有模式底层都强制完成 PoW 计算,自动脚本即使绕过交互挑战仍需付出算力成本。
常见错误码
前端 widget 派发 error 事件
e.detail.error |
含义 |
|---|---|
invalid_site_key |
Site Key 不存在 |
challenge_failed |
服务端无法生成挑战 |
invalid_solution |
答案错误或行为判定为机器人 |
expired |
ticket 过期(超过 10 分钟) |
already_used |
ticket 重放 |
后端 /siteverify 返回的错误
error |
含义 |
|---|---|
missing_fields |
secret 或 response 缺失 |
invalid_secret |
Secret Key 不存在或不匹配 |
invalid_or_expired_token |
token 已被核销或已超时 |
invalid_request |
JSON 格式错误 |
安全建议
- 永远在后端核销 token。前端只负责拿 token,业务决策由后端做。
- Secret Key 不要提交到代码仓库。用环境变量或密钥管理服务。
- 限制 CORS 来源。在管理面板为 Site Key 配置
CORS Origins,只允许你的域名,防止 token 被其他网站盗用。 - 高风险操作建议叠加 rate limit。Guard 自带 IP 限速,但业务层再加一道更稳妥。
- 定期轮换 Secret Key。管理面板里每个 Site Key 都有”Rotate Secret”按钮。
部署提示
- Guard 编译为单个二进制,监听端口默认在
guard.yaml配置。 - 生产环境建议放在反向代理(Nginx / Caddy)后,启用 HTTPS。
- widget 脚本地址
/guard.js会 302 到带 SHA-256 哈希的永久缓存 URL,更新后客户端会自动拿到新版本。 https://guard.sad.kim/guard-worker.wasm是 PoW Web Worker 依赖,需要与guard.js同源。
最小可运行示例
保存为 demo.html,把 Site Key 和服务器地址换成你的:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Guard 接入示例</title>
<script src="https://guard.sad.kim/guard.js" defer></script>
</head>
<body>
<h1>验证示例</h1>
<guard-widget
site-key="YOUR_SITE_KEY"
api="https://guard.sad.kim">
</guard-widget>
<script>
const widget = document.querySelector('guard-widget');
widget.addEventListener('solved', (e) => {
alert('验证通过!\npass token: ' + e.detail.token);
// 真实场景:把 token 随表单提交到你的后端
});
widget.addEventListener('error', (e) => {
alert('验证失败:' + e.detail.error);
});
</script>
</body>
</html>