Telegram Webhook secret_token 与 HMAC:接收端到底该验证什么
Telegram Bot API 的 secret_token 不是 HMAC 签名。通过 setWebhook 配置后,Telegram 会把同一个值放入 X-Telegram-Bot-Api-Secret-Token 请求头,接收端将它与本地配置的令牌比较。UnifyPort 则使用另一种契约:启用签名后,需要用时间戳与原始请求体计算 HMAC-SHA256,再验证 X-Device-Signature。这两套检查不能互换。
先记住这几个区别
- Telegram 的密钥请求头直接携带共享凭证,不是根据 JSON 正文计算出的摘要。
- UnifyPort 签名通过端点的
signing_secret,绑定原始请求体与X-Device-Timestamp。 - 两种接收端都需要 HTTPS、密钥保护和幂等处理。
- 应根据可信的路由配置选择验证方式,不能相信尚未验证的 JSON
provider字段,也不能看哪个请求头存在就用哪种方式。
本文只讨论请求验证。如果你还没决定使用机器人还是既有消息账号,先看 Telegram Bot API webhook 与统一入站 webhook 对比。
Telegram secret_token 实际验证什么
Telegram 官方 Bot API 文档将 secret_token 定义为 setWebhook 的可选参数:长度为 1–256 个字符,只允许 A-Z、a-z、0-9、_ 和 -。配置后,每次 webhook 请求都会在 X-Telegram-Bot-Api-Secret-Token 中携带该值。
如果接收端要求这项保护,缺失或不匹配的请求头就不能进入可信处理队列。完全匹配说明请求方持有配置的值,但并没有建立这个值与请求正文之间的独立密码学关联。
这一点在代理或转发服务处尤其重要:能够读取令牌的组件,也能携带同一令牌提交不同正文。HTTPS 保护传输连接;静态请求头本身无法检测 TLS 终止后发生的正文变化。这是在划分信任边界,不是说官方 Bot API 不适合使用。
建议为 webhook 单独生成凭证,不要复用 bot token,也不要把密钥粘贴到公开抓包服务。访问日志、链路追踪导出和排障截图都应对它脱敏。
静态令牌与正文签名的对照
HMAC 是基于共享密钥的消息认证机制。按照 UnifyPort 文档,接收端需要重新计算指定输入的摘要,而不是把签名请求头与密钥直接比较。
| 问题 | Telegram Bot API webhook | 已启用签名的 UnifyPort webhook |
|---|---|---|
| 配置项 | setWebhook.secret_token | 端点的 signing_secret |
| 验证请求头 | X-Telegram-Bot-Api-Secret-Token | X-Device-Signature |
| 收到的值 | 配置的令牌本身 | 十六进制 HMAC-SHA256 摘要 |
| 是否覆盖正文 | 否 | 是,使用原始字节 |
| 是否覆盖时间戳 | 否 | 是,X-Device-Timestamp |
| 接收端操作 | 比较请求头与令牌 | 重新计算并比较摘要 |
| 是否保证只处理一次 | 否 | 否 |
UnifyPort 的签名输入严格为:
<X-Device-Timestamp>.<raw request body>
时间戳使用 RFC 3339 UTC 格式,中间是一个字面量点号。格式化 JSON、改变空白字符,或者先解析再序列化,都可能改变签名字节。signing_secret 是 HMAC 密钥,不是请求头应直接等于的值。
若 signing_secret 为空,UnifyPort 会关闭签名,不发送 X-Device-Signature。要求签名的接收端应拒绝这种请求,而不是自动切换到别的验证模式。
为两类请求保留独立入口
建议为原生 Telegram 更新与 UnifyPort 事件分别设置应用路由。这里说的是你的应用设计,不是新增的平台 API endpoint。面向东南亚的跨境团队即使把 Telegram、LINE 和 WhatsApp 放进同一个业务队列,也应先在入口完成各自的验证。
- 绑定来源与凭证。 在部署配置中明确每条路由接受哪种验证机制。
- 先验证再分发。 Telegram 校验令牌请求头;已签名的 UnifyPort 请求校验原始正文 HMAC 和时间戳新鲜度。
- 按各自结构校验负载。 不要把原生 Telegram
Update交给期待 UnifyPortmessage.received的处理器。 - 持久化已接受的任务。 请求认证、幂等与业务授权分别处理。
- 记录失败类型而非密钥。 排障日志只说明哪项检查失败,不输出敏感请求头。
不要使用“任一请求头通过即可”的共享中间件。否则,较弱或被意外启用的分支就会成为另一条通过验证的路径。尤其不能让 Telegram 令牌代替 UnifyPort 路由所要求的 HMAC。
第二套契约的实现细节可参考 HMAC 防重放与重试处理。消息 JSON 中的时间字段,不能代替受投递签名保护的时间戳。
上线前应测试哪些边界
以下是建议的测试,不是已经执行的测试结果。请在隔离环境中使用自己控制的凭证验证。
| 测试 | 接收端预期行为 |
|---|---|
| Telegram 请求头缺失或错误 | 在可信处理前拒绝 |
| Telegram 令牌正确,但正文改变 | 单靠令牌检查无法发现;仍需负载校验与可信传输边界 |
| UnifyPort 正文在签名后改变 | 摘要不匹配,拒绝 |
| UnifyPort 签名有效,但时间戳超出配置窗口 | 按新鲜度策略拒绝 |
| 将 Telegram 令牌发送到 UnifyPort 路由 | 拒绝,不切换验证机制 |
| 同一真实普通事件再次投递 | 幂等接受,不重复执行业务动作 |
新鲜度窗口应结合时钟精度和投递条件制定,不要照搬其他平台的值。请求通过验证,也不意味着可以执行消息里要求的任何业务操作。
UnifyPort 的适用边界
UnifyPort 非官方接口为已连接的消息账号提供标准化事件。HMAC 保护的是 UnifyPort 到你的接收端这一段交接,不是 Telegram 原生签名,也不是 Telegram 用户端到端作者身份的证明。
如果产品本来就是 Telegram 机器人,在该入口实现 Telegram 文档中的保护即可,不必仅为改变验证机制而迁移平台。如果通过 UnifyPort 接入账号级或跨渠道队列,就应遵循它的独立契约,并在创建 webhook 端点时启用签名。
常见问题
应该用 Telegram secret_token 计算 HMAC 吗?
验证官方 Bot API 密钥请求头时不需要。应比较该请求头与配置的令牌,不要为直接携带令牌的请求头自行设计正文签名算法。
可以直接比较 X-Device-Signature 与 signing_secret 吗?
不可以。先对文档规定的“时间戳加原始请求体”计算 HMAC-SHA256,再将结果与收到的十六进制签名比较。
任一种机制能阻止重复处理吗?
不能。认证与去重是两件事。应持久化已接受任务,并让下游操作保持幂等;HMAC 本身不会让投递变成只执行一次。
下一步与来源
开发 UnifyPort 接收端时,以 webhook 投递与签名文档作为实现契约。
来源核对日期:2026-09-17。
让消息接入变成一条稳定的产品管线。
先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。