为了防止接口被恶意刷(如自动化脚本、爬虫、暴力请求等),Token 校验是一种常见且有效的安全技术手段。其核心思想是:在客户端发起敏感或高频请求前,必须先从服务端获取一个临时、一次性、有时效性的 Token,后续请求必须携带该 Token 才能被处理。这样可以有效区分“合法用户操作”和“机器自动化行为”。下面就来介绍一下token的具体应用详情:

基本原理

  • 信任前置:服务端只信任自己签发的 Token。
  • 人机识别:正常用户会先加载页面(触发 Token 获取),而机器人往往直接调用接口。
  • 防重放 & 防爆破:Token 具备时效性、一次性或绑定上下文(如 IP、用户会话),无法被重复利用。

典型实现流程(以 Web 或小程序为例)

场景:用户提交订单 / 发送短信验证码 / 登录等敏感操作

步骤 1:前端请求 Token

用户打开页面时(或点击按钮前),前端向服务端请求一个 action_token

GET /api/get-action-token?type=send_sms
步骤 2:服务端生成并返回 Token

服务端生成一个短期有效、绑定上下文的 Token,并存入缓存(如 Redis):

{
  "token": "a1b2c3d4-e5f6-7890",
  "expires_in": 60  // 60秒有效
}

同时在服务端缓存中记录:

Key: action_token:a1b2c3d4-e5f6-7890
Value: {
  user_ip: "1.2.3.4",
  session_id: "sess_abc123",
  action_type: "send_sms",
  used: false,
  expire_at: 1700000060
}
步骤 3:前端携带 Token 调用目标接口

用户点击“发送验证码”时,前端将 Token 放入请求头或参数中:

POST /api/send-sms
Headers: X-Action-Token: a1b2c3d4-e5f6-7890
Body: { phone: "138****1234" }
步骤 4:服务端校验 Token
  • 检查 Token 是否存在;
  • 是否未使用过;
  • 是否在有效期内;
  • 是否与当前请求的 IP / Session 匹配(可选);
  • 是否用于正确的操作类型(如不能用“登录 Token”去发短信)。

校验通过 → 执行业务逻辑,并立即将 Token 标记为已使用(或直接删除)。

校验失败 → 返回错误(如 403 Forbidden),不执行业务。

关键设计要点(提升安全性)

设计项 说明
时效性 Token 通常 30~120 秒有效,防止被长期利用。
一次性 使用后立即失效,防止重放攻击(Replay Attack)。
绑定上下文 可绑定 IP、User-Agent、Session ID,增加伪造难度。
操作类型隔离 不同操作(登录、下单、发短信)使用不同 Token 类型,避免混用。
速率限制配合 即使有 Token,也应对单个用户/IP 的请求频率做限流(如每分钟最多 3 次发短信)。
不暴露在 URL 中 Token 应放在请求头(如 X-CSRF-Token)或 POST Body,避免被日志或 Referer 泄露。
微信小程序 由于运行环境相对封闭,可结合 wx.login() 获取的 code 和 session_key 生成更可信的 Token。

与其他防护机制的结合

Token 校验通常不是孤立使用的,而是与以下技术组合:

  1. 验证码(CAPTCHA):对高风险操作(如大额支付)叠加图形/滑块验证码。
  2. 设备指纹:识别异常设备行为。
  3. 行为分析:检测点击速度、鼠标轨迹等是否符合人类操作。
  4. WAF / API 网关限流:在入口层拦截高频 IP。

局限性与注意事项

  • 不能完全阻止高级 Bot:如果攻击者能完整模拟浏览器行为(包括先请求 Token),仍可能绕过。需结合其他风控手段。
  • 用户体验影响:Token 过期可能导致用户重复操作,需前端做好提示(如“请刷新页面重试”)。
  • 不要用于替代身份认证:Token 是防刷机制,不是身份凭证。用户身份仍需通过 Session / JWT 等验证。

示例:防止短信接口被刷

# 伪代码
def get_sms_token():
    token = generate_uuid()
    redis.setex(f"sms_token:{token}", 60, json.dumps({
        "ip": request.ip,
        "used": False
    }))
    return {"token": token}

def send_sms(token, phone):
    data = redis.get(f"sms_token:{token}")
    if not data or data["used"] or time.now() > data["expire"]:
        raise Error("Invalid token")
    
    redis.delete(f"sms_token:{token}")  # 立即失效
    send_sms_to(phone)

Token 安全审计检查清单(开发/安全团队可用)

检查项 说明 风险等级
所有写操作接口是否要求 Token? POST/PUT/DELETE 类接口(如下单、发短信、修改密码)必须校验
Token 是否由服务端生成? 禁止前端生成或预测(如时间戳、自增 ID)
Token 是否具备时效性? 建议 30~120 秒,超时自动失效
Token 是否一次性使用? 使用后立即标记为已用或删除,防止重放
Token 是否绑定上下文? 如用户 ID、Session、IP、User-Agent(至少绑定用户)
Token 存储是否安全? 使用 Redis/Memcached,禁止明文存 Cookie 或 URL
校验失败是否记录日志? 记录 IP、Token、时间,用于风控分析
是否存在“跳过 Token 校验”的调试开关? 如 ?debug=true 绕过校验,上线前必须关闭
Token 是否通过安全通道传输? 放在 Header(如 X-CSRF-Token),避免 URL 或 Referer 泄露
下单等核心逻辑是否重新校验业务数据? 即使 Token 有效,仍需从 DB 读取商品价格、库存

常见绕过场景 & 修复建议

绕过方式 描述 修复方案
跳过 Token 获取步骤 直接调用提交接口,猜测 Token 格式 强制所有提交必须携带有效 Token,无默认值
Token 可预测 使用时间戳、自增 ID 作为 Token 使用 UUID v4 或 SecureRandom 生成
未绑定用户 A 用户的 Token 被 B 用户使用 Token 缓存中记录 userId,校验时比对
校验在业务后执行 先扣库存再校验 Token 校验必须在最前面
Token 未失效 下单失败后 Token 仍可重试 无论成功失败,Token 一旦使用即失效
测试代码残留 开发环境绕过校验的开关未关闭 上线前清理,或通过配置中心控制

总结

Token 校验的本质是“挑战-响应”机制:服务端先发出一个临时通行证,只有能正确出示该通行证的请求才被受理。它成本低、效果好,是防刷接口的第一道防线。另外Token 不是“加了就行”,而是“用对才安全”,我们要对token进行安全审核,防止 token 机制被绕过,筑牢业务安全防线。重点核心关注以下三点:

  • 自动化扫描 + 人工深度审查 结合;
  • 关注“校验时机”和“数据权威性”;
  • 假设所有客户端输入都是恶意的。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐