Cloud Functions中Webhook请求的HMAC256签名验证问题
解决Cloud Functions中Webhook HMAC签名验证不匹配的问题
你遇到的核心问题是计算签名时误用了解析后再序列化的JSON,而非原始请求体字节,再加上签名头格式处理的小细节偏差,导致两者哈希结果完全不匹配。下面是一步步的修复方案:
1. 核心问题:用原始请求体替代解析后JSON
当你调用request.get_json()把请求体解析成Python字典后,再用json.dumps()转回字符串,生成的内容大概率和源端发送的原始请求体不一致——JSON序列化的空格、键的排序、甚至引号处理都可能有差异,而HMAC是基于精确字节流计算的,哪怕一个空格不同,最终哈希值都会完全不同。
正确的做法是直接获取请求的原始字节:request.data,这个变量保存了Webhook发送过来的未经任何解析的原始请求体,直接用它来计算HMAC就能保证字节一致性。
2. 处理签名头的格式
源端返回的签名是sha256=<key>格式,你需要先把<key>部分提取出来,再和自己生成的签名对比,注意大小写统一(比如头里是小写sha256=,要避免和大写的SHA256混淆)。
3. 修正后的完整代码
这里是调整后的代码,解决了上述所有问题:
import firebase_admin from firebase_admin import credentials from firebase_admin import firestore import hmac, hashlib # Use the application default credentials cred = credentials.ApplicationDefault() firebase_admin.initialize_app(cred, { 'projectId': 'my-project-id', }) db = firestore.client() def webhook(request): myToken = "2ET#####################sB" #Secret from origin # 提取签名头中的哈希值 signature_header = request.headers.get('BTCPAY-SIG', '') if not signature_header.startswith('sha256='): return f'Invalid signature format', 400 expected_signature = signature_header.split('=')[1] # 获取原始请求体字节 raw_body = request.data generated_signature = _generate_signature(raw_body, myToken) print(f"Received signature: {expected_signature}") print(f"Generated signature: {generated_signature}") print('Validate?', generated_signature == expected_signature) if generated_signature != expected_signature: return f'Invalid signature', 403 return f'Hello World!' def _generate_signature(raw_data, myToken): # 密钥转字节,用utf-8编码(和源端保持一致) key_bytes = bytes(myToken, 'utf-8') # raw_data已经是字节类型,直接传入计算HMAC return hmac.new(key_bytes, raw_data, hashlib.sha256).hexdigest()
额外调试建议
如果调整后还是不匹配,可以做这些排查:
- 打印
raw_body的长度和内容(注意屏蔽敏感信息),和源端确认他们发送的原始请求体是否完全一致 - 确认源端使用的JSON序列化规则:比如是否开启了
sort_keys=True,或者用了separators=(',', ':')的紧凑无空格格式。如果Cloud Functions转发请求时自动修改了请求体格式,可尝试用json.dumps(request_json, sort_keys=True, separators=(',', ':'))转成字节再计算(这是备选方案,优先用原始请求体) - 核对密钥是否完全一致,没有额外的空格、换行符或大小写错误
内容的提问来源于stack exchange,提问作者Santiago Bocel
相关产品推荐
相关产品推荐

