跳到主要内容

服务端校验

校验函数是业务接口的第一道门,而不是最后一道装饰。

位置​

请求进入 → 校验令牌 → 参数校验 → 查询账号 → 业务处理
↑
失败就在这里返回

对于登录接口,这意味着:不因为一个未通过验证的请求而去比对密码;对于找回密码接口,意味着不因为一个未通过验证的请求而去查邮箱、发信。

校验内容​

检查失败原因
签名有效令牌被篡改或用了错误的密钥
专用标记存在普通会话凭证被拿来冒充验证令牌
用途匹配令牌来自另一张表单
未过期令牌在客户端停留过久
挑战已正确兑换绕过求解直接构造令牌
信息

同密钥,不同用途 — 验证令牌与会话令牌可能由同一套签名机制签发。因此校验函数必须额外要求令牌里带有「这是验证令牌」的专用标记,只验签是不够的。

失败分支​

统一返回 403 与简短提示,不要区分「签名错」「用途错」「过期」给客户端。细分错误会帮助攻击者探测实现细节。

const res = await fetch('https://your-nscha-host/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: req.body.nschaToken, scope: 'login' }),
})

if (!res.ok) {
return respond(403, { error: '人机验证未通过,请重试' })
}
// 通过之后才查询账号

失败统一返回 403 { ok: false },不细分原因。若不想多一次 HTTP,也可以把 src/protocol.ts 的 verifyToken() 放进自己的服务,用同一把 CAP_TOKEN_SECRET 本地验签。

一次性的是兑换,不是令牌​

兑换接口用进程内的同步占位记录保证同一份挑战只能兑换一次。而兑换出来的令牌,在有效期内不做全局一次性消费 —— 校验函数只验签、验时效与用途。

这一点在多实例部署时尤其重要,详见多实例部署与诚实边界。