HMAC-SHA256签名验证失败,求助排查Ruby代码问题
以下是针对你的代码和场景的具体排查方向:
1. 请求头字段的大小写与存在性问题
HTTP头字段名大小写不敏感,但Rails的request.headers在部分环境下可能保留原始大小写。你代码中使用request.headers['User-agent'](小写agent),但实际请求头可能是User-Agent(大写A),这会导致获取空值,破坏签名输入的完整性。
建议改用兼容大小写的获取方式,或统一使用标准头名:
user_agent = "\"user-agent\": #{request.headers['User-Agent'] || request.headers['user-agent']}\n"
同时要确保Content-Length、Content-Type、Host、Date、Digest这些依赖字段都存在,避免因缺失字段导致签名输入错误。
2. @signature-params的参数顺序与格式匹配
根据规范,@signature-params的内容必须与请求头Signature中的参数完全一致,包括参数顺序、引号格式、分号位置。
比如检查请求头Signature里的keyid、algorithm顺序是否和你代码生成的一致。如果Manager One实际生成的@signature-params中algorithm在keyid之前,你的代码顺序相反就会导致签名不匹配。
3. Request-Target的准确性
确保request.path完全匹配实际请求路径:
- HTTP方法必须为小写(如
post),你的代码已符合要求; - 如果请求带查询参数,
request.path不包含参数,需拼接request.query_string:request_target = "\"(request-target)\": post #{request.path}#{request.query_string.empty? ? '' : "?#{request.query_string}"}\n"
4. Digest头的正确性验证
Digest是签名输入的核心部分,需确保请求体哈希值与头中Digest一致。可临时添加代码验证:
calculated_digest = "SHA-256=#{Base64.strict_encode64(OpenSSL::Digest::SHA256.digest(request.raw_post))}" puts "Received Digest: #{request.headers['Digest']}" puts "Calculated Digest: #{calculated_digest}"
如果两者不一致,说明请求体传输中被修改,或你多次读取request.raw_post导致后续读取为空。
5. Signature头解析的鲁棒性
你的正则匹配/signature=\"([^\"]+)\"/依赖参数位置和格式,若Signature头参数顺序变化或有额外空格,可能匹配失败。建议改用更可靠的解析方式:
def provided_signature_base64 return nil unless request.headers['Signature'] request.headers['Signature'].split(',').each do |param| key, value = param.strip.split('=', 2) return value.gsub(/\"/, '') if key == 'signature' end nil end
6. 共享密钥的正确性
确认ENV["MANAGER_ONE_SECRET_SIGNATURE"]的值与Manager One提供的完全一致,无额外空格、换行或转义字符。可临时打印对比:
puts "Shared Secret: #{ENV["MANAGER_ONE_SECRET_SIGNATURE"]}"
7. 换行符与编码问题
签名输入中的换行必须是单个\n,不能是\r\n(Windows换行符)。你的代码使用\n正确,但要确保字符串拼接时未引入额外换行或空格。同时确保所有字符串(共享密钥、签名输入)为UTF-8编码,避免编码不一致导致哈希计算错误。
内容的提问来源于stack exchange,提问作者raphael-allard

