如何在Fastify中验证Twilio签名?求成功实现经验指导
Fastify中Twilio签名验证失败的排查与修复方案
以下是针对你代码中可能存在的问题,给出的具体修复方案和排查要点:
核心问题排查点
- 反向代理下的URL构造错误:如果你的Fastify服务器部署在Nginx等反向代理后,
req.protocol和req.headers.host可能指向代理服务而非Twilio实际访问的地址,导致签名验证时URL不匹配。 - 原始Body处理不规范:Fastify的
rawBody默认是Buffer类型,直接转字符串可能存在编码问题;且Twilio发送的参数可能包含重复键,Object.fromEntries会覆盖这些值,导致验证失败。 - 参数解析逻辑缺失:Twilio的签名验证需要完整保留所有请求参数(包括重复字段),直接用
URLSearchParams转对象会丢失多值参数。
修复后的代码示例
验证函数修改
import { validateRequest } from 'twilio'; export const verifyTwilioSignature = (req: FastifyRequest, reply: FastifyReply, done: () => void) => { const twilioSignature = req.headers['x-twilio-signature'] as string; // 适配反向代理场景,优先取转发头信息构造真实URL const protocol = req.headers['x-forwarded-proto'] || req.protocol; const host = req.headers['x-forwarded-host'] || req.headers.host; const fullUrl = `${protocol}://${host}${req.raw.url}`; const authToken = getEnvVar("TWILIO_AUTH_TOKEN"); if (!authToken || !twilioSignature) { reply.status(403).send({ error: "缺少Twilio签名或AuthToken" }); return; } // 确保原始Body正确转成UTF-8字符串 const rawBody = typeof req.rawBody === 'string' ? req.rawBody : req.rawBody?.toString('utf-8'); if (!rawBody) { reply.status(403).send({ error: "无法获取请求原始Body" }); return; } // 正确解析参数,保留重复字段的数组格式 const params = new URLSearchParams(rawBody); const paramsObj: Record<string, string | string[]> = {}; params.forEach((value, key) => { if (paramsObj[key]) { paramsObj[key] = Array.isArray(paramsObj[key]) ? [...paramsObj[key] as string[], value] : [paramsObj[key] as string, value]; } else { paramsObj[key] = value; } }); try { const isValid = validateRequest(authToken, twilioSignature, fullUrl, paramsObj); if (!isValid) { reply.status(403).send({ error: "Twilio签名无效" }); return; } done(); } catch (err) { reply.status(403).send({ error: "签名验证过程出错" }); return; } };
路由配置确认
确保路由的rawBody配置生效,同时避免其他中间件篡改原始请求体:
export const twilioWebhookRoutes: FastifyPluginCallback = (app, _, done) => { app.post("/webhook/twilio/sms", { config: { rawBody: true }, // 若使用了formbody插件,可临时禁用body解析确保rawBody完整 // bodyParser: false, preHandler: verifyTwilioSignature, schema: twilioSmsWebhookSchema, handler: twilioSmsWebhook, }); done(); };
额外验证步骤
- 确认
TWILIO_AUTH_TOKEN与Twilio控制台中的值完全一致,无多余空格或拼写错误。 - 用Twilio提供的签名验证工具,对比你构造的
fullUrl和参数是否与Twilio发送的完全匹配。 - 若服务器在反向代理后,确保代理服务已传递
X-Forwarded-Proto和X-Forwarded-Host头。
内容的提问来源于stack exchange,提问作者Steytz
相关产品推荐
相关产品推荐

