使用AWS API Gateway验证GitHub Webhook Secret签名不匹配问题求助
错误原因分析
- 核心错误:对payload的序列化逻辑错误。AWS API Gateway传递到Lambda的
event['body']默认是GitHub发送的原始JSON字符串,代码中对该字符串再次调用json.dumps()会额外添加外层引号、转义内部特殊字符,导致参与签名计算的payload和GitHub实际发送的内容完全不一致,直接造成签名校验失败。 - 缺失Base64解码逻辑:如果API Gateway开启了二进制payload自动编码配置,
event['body']会是Base64编码后的字符串,需要先解码才能参与签名计算。 - 签名对比方法不安全:直接用
==对比哈希签名存在计时攻击风险,建议使用Python内置的安全对比方法。
修正后代码
import json import hmac import hashlib import re import base64 GITHUB_SECRET = 'HELLO WORLD' # 替换为你在GitHub配置的实际secret def lambda_handler(event, context): print("Lambda execution starting up...") # 处理入站签名 incoming_signature = re.sub(r'^sha1=', '', event['headers']['X-Hub-Signature']) # 切换为X-Hub-Signature-256时使用下面这行替换上面的签名提取逻辑 # incoming_signature = re.sub(r'^sha256=', '', event['headers']['X-Hub-Signature-256']) # 处理原始payload,先判断是否是Base64编码 if event.get('isBase64Encoded', False): raw_body = base64.b64decode(event['body']).decode('utf-8') else: raw_body = event['body'] # 直接用原始payload计算签名,不要二次序列化 calculated_signature = calculate_signature(GITHUB_SECRET, raw_body.encode('utf-8')) print("Incoming sig:", incoming_signature) print("calculated_signature:", calculated_signature) # 用安全方法对比签名 if not hmac.compare_digest(incoming_signature, calculated_signature): print('Unauthorized attempt') return { 'statusCode': 403, 'body': json.dumps('Forbidden') } print('Request successfully authorized') # 后续业务逻辑 return { 'statusCode': 200, 'body': json.dumps(f'Work in progress') } def calculate_signature(github_secret, github_payload): signature_bytes = bytes(github_secret, 'utf-8') digest = hmac.new(key=signature_bytes, msg=github_payload, digestmod=hashlib.sha1) # 切换为256校验时,把上面的digestmod改为hashlib.sha256即可 signature = digest.hexdigest() return signature
额外注意事项
- 不要在代码中硬编码secret,建议使用AWS Secrets Manager或Lambda环境变量存储敏感信息。
- 确认API Gateway的集成请求配置中没有修改原始payload,不要开启映射模板对body做额外转换。
内容的提问来源于stack exchange,提问作者tomarv2
相关产品推荐
相关产品推荐

