无法验证来自Bot的Slack Events API请求问题排查
Slack Bot消息触发Events API时签名验证失败(Signature Mismatch)排查与修复
问题核心
开发集成ChatGPT的Slack反馈机器人时,用户发送消息触发的Events API请求能通过签名验证,但Bot自身发送消息触发的event_callback请求出现签名不匹配错误。当前因无法获取原始请求体(rawBody始终为undefined),只能用JSON.stringify(request.body)替代,导致签名计算不一致。
关键原因分析
Slack的请求签名是基于原始HTTP请求体的字节内容生成的,而JSON.stringify(request.body)会破坏原始请求体的一致性:
- 键顺序差异:JavaScript对象解析后再序列化为JSON时,键的顺序可能与Slack发送的原始JSON不一致
- 格式差异:序列化过程可能自动添加/移除空格、转换值类型(如空字符串转
undefined) - 字段修改:中间件可能对解析后的
request.body进行了增删字段操作
这些差异会导致签名计算用的basestring与Slack生成的不一致,最终触发签名不匹配错误。
修复方案
方案1:优先获取原始请求体(推荐)
不同框架获取rawBody的方式不同,以下是常见框架的实现示例:
- Express:通过监听请求流手动收集原始内容,再解析JSON
import express from 'express'; const app = express(); // 先收集rawBody,再解析JSON app.use((req, res, next) => { let rawBody = ''; req.on('data', chunk => rawBody += chunk.toString()); req.on('end', () => { (req as any).rawBody = rawBody; next(); }); }); app.use(express.json());
获取到rawBody后,直接用它作为验证函数的body参数,无需序列化。
方案2:强制JSON序列化匹配原始格式
若确实无法获取rawBody,需严格保证序列化后的字符串与Slack原始请求体一致:
- 固定键顺序:按照Slack发送的
event_callback请求体的键顺序,手动指定序列化顺序 - 避免类型转换:确保解析后的
request.body字段类型与原始请求体完全一致(如空字符串不能变为undefined)
调整调用代码如下:
// 定义Slack event_callback请求体的固定键顺序 const slackEventKeys = [ 'token', 'team_id', 'context_team_id', 'context_enterprise_id', 'api_app_id', 'event', 'type', 'event_id', 'event_time', 'authorizations', 'is_ext_shared_channel', 'event_context' ]; // 优先用rawBody,否则用固定键顺序序列化 const bodyToVerify = rawBody as string || JSON.stringify(request.body, slackEventKeys); const verificationOptions: SlackRequestVerificationOptions = { signingSecret, body: bodyToVerify, headers: { 'x-slack-signature': headers['x-slack-signature'] as string, 'x-slack-request-timestamp': headers['x-slack-request-timestamp'] as string, }, millisecondsNow: Date.now(), }; verifySlackRequest(verificationOptions);
验证排查步骤
- 对比原始请求体(可通过抓包或日志获取)与
JSON.stringify(request.body)的输出,检查是否存在键顺序、格式或值的差异 - 打印签名计算用的
basestring,确认与Slack文档中描述的格式一致(v0:timestamp:raw_body) - 检查
request.body是否被中间件修改过(如添加额外字段、转换值类型)
内容的提问来源于stack exchange,提问作者sss1703
相关产品推荐
相关产品推荐

