Starling Bank WebHook签名验证失败:crypto与bodyParser使用疑问
问题分析与解决方案
看起来你的签名验证失败的核心问题在于没有获取到Starling Bank发送的原始请求体,以及中间件配置和代码细节上的小问题。让我一步步帮你排查和修复:
1. 核心问题:bodyParser.json() 默认不会保留原始请求体
当你使用bodyParser.json()时,它会读取并解析请求体为JSON对象,但默认不会保留原始的请求体字符串。而Starling的签名是基于未修改的原始JSON payload计算的——如果你用解析后的req.body或者不正确的rawBody,哈希结果必然和WebHook发送的签名不一致。
2. 修复步骤
步骤1:配置bodyParser保留原始请求体
修改你的Express应用中bodyParser的配置,添加verify选项来把原始请求体保存到req.rawBody:
app.use(bodyParser.json({ verify: (req, res, buf) => { // 将原始请求体Buffer转换为UTF-8字符串保存 req.rawBody = buf.toString('utf8'); } }));
这个配置会让bodyParser在解析JSON的同时,完整保留原始请求体字符串,这是计算正确签名的关键前提。
步骤2:修正签名计算代码
先修复代码中的语法错误(多了一个右括号),再确保用原始请求体和密钥正确计算哈希:
// middleware.js const functions = require('firebase-functions'); import * as crypto from 'crypto'; // 请确保config已正确导入 import config from './path-to-your-config'; export const auth = (req, res, next) => { try { // 先检查原始请求体是否存在 if (!req.rawBody) { return res.status(400).send('Missing raw request body'); } // 按Starling规则计算签名:密钥 + 原始请求体的SHA512摘要,再Base64编码 const hash = crypto.createHash('sha512'); // 指定UTF-8编码,避免字符串拼接时的编码不一致问题 hash.update(config.starling.key + req.rawBody, 'utf8'); const computedSignature = hash.digest('base64'); const incomingSignature = req.header('X-Hook-Signature'); if (computedSignature !== incomingSignature) { return res.status(403).send('Invalid webhook signature'); } // 签名验证通过,继续执行后续逻辑 next(); } catch (err) { console.error('Signature verification error:', err); return res.status(500).send('Internal server error'); } }
步骤3:额外检查点
- 确认密钥正确性:确保
config.starling.key是Starling Bank提供的WebHook密钥,没有多余的空格、换行或拼写错误。 - 避免提前消耗请求体:确保没有其他中间件(比如自定义日志中间件)在
auth中间件之前修改或消耗了请求体。 - 编码一致性:Starling发送的请求体是UTF-8编码,我们用
buf.toString('utf8')保证了编码匹配,不要使用其他编码格式。
为什么之前的代码不对?
- 你依赖的
req.rawBody在默认bodyParser.json()配置下是不存在的,自然无法获取正确的原始请求体。 - 代码存在语法错误(多了一个右括号),可能导致运行时异常。
- 没有明确指定编码,可能导致字符串拼接时出现编码不一致的问题。
按照上面的步骤修改后,应该就能正确验证Starling WebHook的签名了。
内容的提问来源于stack exchange,提问作者Pieter
相关产品推荐
相关产品推荐

