Node Red对接Xero Webhooks签名校验异常:如何获取原始HTTP请求体
Node-RED 接收Xero Webhook时HMAC签名校验失败问题与解决方案
- 持续调试相关加密逻辑多日,当前正在搭建Node-RED方案接收Xero推送的Webhook事件并写入自定义应用。
- 开发过程中遇到大量载荷序列化、哈希计算规则相关问题,最初参考公开的字符串修正代码,尝试将解析后的载荷还原为原始请求体格式,代码如下:
let msgPayloadRaw = JSON.stringify(msg.payload).split(':').join(': ').split(': [').join(':[').split(',"entropy"').join(', "entropy"');
- 后续编写JS逻辑,基于Webhook密钥生成SHA256 Base64格式的HMAC哈希值,用于和请求头携带的签名值做比对,代码如下:
var cryptojs = context.global.cryptojs; const webhookKey = 'MyWebhookKeyHere'; let msgPayloadRaw = JSON.stringify(msg.payload).split(':').join(': ').split(': [').join(':[').split(',"entropy"').join(', "entropy"'); let bdata = new Buffer(msgPayloadRaw).toString(); let ciphertext = cryptojs.HmacSHA256(bdata, webhookKey ); let base64encoded = cryptojs.enc.Base64.stringify(ciphertext); msg.payload = base64encoded; return msg;
- 原预期上述代码可正常运行,但实际出现异常:Xero Webhook的意图校验状态先变为“OK”,数秒后就返回投递失败提示,具体报错内容如下:
Retry We haven’t received a successful response on the most recent delivery attempt and will retry sending with decreasing frequency for an overall period of 24 hours. Response not 200. Learn more Last sent at 2022-06-22 11:48:28 UTC
问题根因
- Node-RED的HTTP输入节点默认会自动将请求体解析为JSON对象,而Xero的Webhook签名校验规则要求:计算HMAC的输入必须和原始发送的请求体字节完全一致,才能得到匹配的签名值。
- Xero发送的原始请求体存在固定格式的空格,示例如下:
{"events":[],"firstEventSequence": 0,"lastEventSequence": 0, "entropy": "IVMMHNWPBAZYRZJRCUAQ"}
- 直接对Node-RED解析后的JSON对象执行
JSON.stringify(msg.payload)会丢失部分格式空格,生成的字符串示例如下:
{"events":[],"firstEventSequence":0,"lastEventSequence":0, "entropy":"IVMMHNWPBAZYRZJRCUAQ"}
- 两个字符串语义完全一致,但字节层面的空格差异会导致哈希计算结果完全不同,最终签名校验失败。此前使用的字符串替换临时方案无法覆盖所有请求场景,存在兼容漏洞。
可行实现方案
方案1:修改HTTP输入节点配置,直接获取原始请求体
- 双击打开对应HTTP输入节点的配置页面
- 找到请求体解析相关配置项,取消勾选自动解析JSON请求体的选项
- 配置完成后,节点输出的
msg.payload就是未经任何格式处理的原始请求体内容,直接使用该值计算HMAC,即可得到和Xero请求头完全匹配的签名值
提示:如果后续业务流程需要使用结构化的JSON数据,可在签名校验逻辑完成后,手动执行
JSON.parse(msg.payload)完成解析,不影响后续逻辑开发。
方案2:配置HTTP中间件缓存原始请求体
如果不方便修改现有HTTP节点的解析配置,可以通过Node-RED全局中间件,在请求被节点解析前提前缓存原始请求体:
- 打开Node-RED根目录下的
settings.js配置文件 - 找到
httpNodeMiddleware配置段,添加如下逻辑,将原始请求体缓存到请求对象上:
httpNodeMiddleware: function(req, res, next) { let rawBodyChunks = []; req.on('data', chunk => { rawBodyChunks.push(chunk); }); req.on('end', () => { req.rawBody = Buffer.concat(rawBodyChunks).toString(); next(); }); },
- 重启Node-RED生效配置后,后续签名校验逻辑直接读取
msg.req.rawBody作为哈希计算的输入即可,无需再做任何字符串替换修正。
内容的提问来源于stack exchange,提问作者efstaska
相关产品推荐
相关产品推荐

