Nim开发Slack bot时Jester+Nimcrypto签名验证不匹配问题求助
核心问题修复方案
1. 签名前缀处理
Slack返回的X-Slack-Signature头固定为v0=xxxx格式,你当前直接将整个头值传入fromHex的逻辑完全错误,v0=不属于十六进制字符,会导致解析出的摘要完全失真。
抗时序对比时确实需要跳过前缀,直接取头值去掉前3位v0=的部分做十六进制解析即可,nimcrypto中MDigest的==运算符本身实现了抗时序对比逻辑,可以直接使用。
2. Jester请求body预处理问题
你的推测正确,Jester默认会对application/x-www-form-urlencoded类型的请求做自动表单解析,解析完成后request.body会被置空,你拿到的不是原始请求载荷。
解决方案有两种:
- 全局关闭Jester的自动表单解析,在路由定义前添加配置
settings.formParse = false - 直接读取原生Httpbeast请求的body:Jester的
Request对象内置req字段对应原生Httpbeast请求,通过$request.req.body即可拿到未被修改的原始载荷
3. 隐藏问题:签名密钥读取逻辑错误
你当前将signingSecret定义为const是完全错误的,const为编译期求值,编译阶段就会读取当时的环境变量值写死到二进制中,运行时不会读取系统实际的环境变量,需要改为let做运行时读取。
修正后核心代码
import jester import dotenv import os, strutils, times import nimcrypto const timestampHeader = "X-Slack-Request-Timestamp" const slackSignatureHeader = "X-Slack-Signature" const signatureVersion = "v0" # 改为运行时读取环境变量 let signingSecret = os.getEnv("SLACK_SIGNING_SECRET") proc isTimestampRecent(timestamp: int): bool = abs(getTime().toUnix - timestamp) <= (60 * 5) proc verifySignature*(request: Request): bool = if (not request.headers.hasKey timestampHeader) or (not request.headers.hasKey slackSignatureHeader): return false let timestamp = request.headers[timestampHeader].parseInt if not timestamp.isTimestampRecent(): return false # 读取原始请求body let rawBody = $request.req.body let baseString = signatureVersion & ':' & $timestamp & ':' & rawBody let mySignature = sha256.hmac(signingSecret, baseString) # 去掉签名前缀后再解析 let slackSigRaw = request.headers[slackSignatureHeader].stripPrefix(signatureVersion & "=") let slackSignature = MDigest[256].fromHex(slackSigRaw) return mySignature == slackSignature # 关闭自动表单解析,避免body被清空 settings.formParse = false routes: post "/slack/events": if verifySignature(request): resp "ok" else: resp Http403, "invalid signature"
调试思路
如果修改后还是校验失败,可按以下步骤排查:
- 打印请求timestamp、原始body、生成的baseString、本地计算的签名、Slack返回的签名值,逐段对比确认哪个环节数据异常
- 用curl构造模拟请求,手动计算签名后发送到服务端,排除网络传输或Slack端配置错误
内容的提问来源于stack exchange,提问作者zerodahero
相关产品推荐
相关产品推荐

