跳转到主要内容

在后端验签 MiniApp initData(HMAC、TTL、错误码)

MPChat MiniApp initData 的后端 HMAC-SHA256 验签流程,含 Node.js 示例、推荐错误码、TTL,以及当前不支持的回流能力。

先验签再信任。对排序后的字段计算 HMAC-SHA256,常量时间比较,再校验 auth_date 新鲜度。之后才读取 userchatminiapp_id


总览

前端发送原始 initData 字符串。后端必须用 bot token 验证其签名后才能使用任何字段。用户信息来自验签后的 initData.user;当前阶段 MPChat 不签发平台 access token。

验签步骤

  • initData 当查询串解析;取出 hash 并从签名集合中移除。

  • 其余字段按 key 排序,格式化为 key=value 行,用换行连接成 data_check_string

  • 密钥 = HMAC-SHA256(key="WebAppData", message=bot token)。

  • 期望 hash = HMAC-SHA256(key=密钥, message=data_check_string)。

  • 常量时间比较期望 hash 与收到的 hash

  • 校验 auth_date 新鲜度(默认 TTL 300 秒)。

  • 若把请求绑定到某个 MiniApp,校验签名的 miniapp_id 是否匹配。

Node.js 示例

import { createHmac, timingSafeEqual } from "node:crypto";function buildCheckString(params) {
  return [...params.entries()]
    .filter(([k]) => k !== "hash")
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([k, v]) => `${k}=${v}`)
    .join("\n");
}export function verifyInitData(initData, botToken, { ttlSeconds = 300, miniappId } = {}) {
  const params = new URLSearchParams(initData);
  const hash = params.get("hash");
  const authDate = Number(params.get("auth_date"));
  if (!hash || !Number.isFinite(authDate)) throw new Error("INIT_DATA_INVALID");  const secret = createHmac("sha256", "WebAppData").update(botToken).digest();
  const expected = createHmac("sha256", secret).update(buildCheckString(params)).digest("hex");  const a = Buffer.from(hash, "hex");
  const b = Buffer.from(expected, "hex");
  if (a.length !== b.length || !timingSafeEqual(a, b)) throw new Error("INIT_DATA_INVALID");  if (Math.floor(Date.now() / 1000) - authDate > ttlSeconds) throw new Error("INIT_DATA_INVALID");
  if (miniappId && params.get("miniapp_id") !== miniappId) throw new Error("MINIAPP_FORBIDDEN");  return JSON.parse(params.get("user"));
}

推荐错误码

场景

错误码

initData 缺失/格式错误/被篡改/过期

INIT_DATA_INVALID

MiniApp 不存在

MINIAPP_NOT_FOUND

MiniApp 已停用

MINIAPP_DISABLED

miniapp_id 不属于期望的 Bot/上下文

MINIAPP_FORBIDDEN

当前不支持

当前阶段没有完整的客户端回流:web_app_datasendDataanswerWebAppQuery,以及平台 access token 交换。后端无需调用平台 Bearer-token 接口获取用户信息。

相关文章

错误响应与日志中绝不能包含 bot token、完整 initData、数据库 ID 或堆栈。

这是否解答了您的问题?