本文目录导读:

验证请求签名通常用于确保数据在传输过程中未被篡改,并验证发起方身份的真实性,最常见的场景是API接口签名(如微信支付、阿里云API)。
验证签名的基本思路是:使用与签名方相同的算法、密钥和参数顺序,重新计算一次签名,然后将计算出的签名与请求携带的签名进行比对,如果一致,则验证通过。
下面是一个通用的、标准的验证流程,主要分为“准备工作”和“核心计算”两部分。
第一部分:准备工作
在开始验证前,你需要拿到以下东西:
- 请求数据:请求体(Body)或请求中的参数。
- 签名值:请求中携带的签名,通常在请求头
X-Sign或某个特定参数sign中。 - 签名算法:对方使用的算法,如
MD5、SHA1、SHA256(最常用),或HMAC-SHA256。 - 签名密钥:这是最关键的一点。 验证方必须持有与签名方相同的密钥,这个密钥通常是在双方协商时约定好的(如
AppSecret)。 - 参数排序规则:签名方和验证方必须使用完全相同的参数顺序进行拼接。
第二部分:通用验证步骤(核心流程)
假设我们用的是最常见的 HMAC-SHA256 或 SHA256 签名方式(发送 POST 请求,参数包含 timestamp, nonce, data)。
第1步:提取签名值
从请求头或请求参数中拿到对方提供的签名。
received_sign = "abc123def..."
第2步:获取签名原始字符串(最重要的一步)
你需要把所有需要参与签名的参数(包括时间戳、随机数、请求体内容等)按照特定的顺序拼成一个字符串。
常见规则:
- 排序:通常按参数名字典序(ASCII码升序)排序。
- 拼接:将
key=value用&连接起来。 - 特殊处理:有些场景会拼接 API 路径,或直接将整个请求体 JSON 字符串作为原始字符串。
示例:
假设参数有 timestamp=1718000000, nonce=abc123, data={"user":"1"}。
- 按字典序排序后:
data,nonce,timestamp。 - 拼接字符串:
data={"user":"1"}&nonce=abc123×tamp=1718000000 - (可选)有些规则会在前面或后面拼接
AppSecret或 API Key。
第3步:生成期望的签名
使用与签名方完全相同的算法和密钥,对上一步生成的原始字符串进行计算。
场景A:使用对称密钥(HMAC-SHA256)
这是目前最安全、最推荐的方式,你需要有 AppSecret 作为密钥。
import hmac
import hashlib
# secret_key = "your_app_secret_here"
# raw_string = "data=...&nonce=...×tamp=..."
expected_sign = hmac.new(
key=secret_key.encode('utf-8'),
msg=raw_string.encode('utf-8'),
digestmod=hashlib.sha256
).hexdigest()
场景B:使用简单的 MD5/SHA256 + 密钥拼接 这是较老的方式,不安全,已逐渐被淘汰。
import hashlib
# secret_key = "your_app_secret_here"
# raw_string = "data=...&nonce=...×tamp=..."
# 规则:先拼接字符串,再拼接密钥
combined_string = raw_string + "&key=" + secret_key
expected_sign = hashlib.sha256(combined_string.encode('utf-8')).hexdigest()
# 或者 hashlib.md5(combined_string.encode('utf-8')).hexdigest()
第4步:比较签名
将计算出的 expected_sign 与请求携带的 received_sign 进行严格(大小写敏感)比较。
if expected_sign == received_sign:
print("✅ 签名验证通过,请求合法。")
# 继续处理业务逻辑
else:
print("❌ 签名验证失败,请求可能被篡改。")
# 拒绝请求,返回 401 Unauthorized
第三部分:不同场景的验证细节
标准 RESTful API(如阿里云、腾讯云)
- 验证方:服务端。
- 密钥:通常是
AccessKey Secret。 - 特殊要求:除了比较签名,还要验证时间戳(防止重放攻击),如果请求时间戳与服务器当前时间相差超过5分钟,直接拒绝,无需验签。
网页前端(Web 端)支付回调
- 验证方:你的后端服务器(接收支付平台的回调)。
- 密钥:支付平台提供的
Key。 - 注意事项:永远不要在前端(JavaScript)中验证签名! 密钥会暴露。
微信/支付宝回调(典型例子)
微信支付的回调验证方式是:
- 先读取 HTTP 头部的
Wechatpay-Signature。 - 将
Wechatpay-Timestamp、Wechatpay-Nonce、请求体 Body 拼在一起。 - 使用微信平台证书里的公钥(非对称加密)进行验签。
- 验证通过后,还需要解密敏感数据。
核心区别:微信使用的是非对称加密(RSA),而不是共享密钥,验证方拿到的是公钥,只能用来验证,无法伪造签名。
总结与关键提醒
| 检查项 | 说明 |
|---|---|
| 密钥安全 | 密钥(Secret)储存在你的后端配置文件中,永远不要硬编码在前端或暴露给用户。 |
| 排序一致 | 签名方的排序规则(如字典序)必须与验证方完全一致,否则验签必败。 |
| 空值与编码 | 注意对参数值进行 URLEncode(常见于 HTTP GET 请求)或保持原始字符串(常见于 Body),规则要统一。 |
| 时间复杂度 | 使用 HMAC 比直接拼接密钥要安全得多,建议优先选择。 |
| 重放攻击 | 验签通过后,还要检查时间戳和 Nonce(一次性随机数),防止别人记录下你的请求报文反复调用。 |
一句话流程:对方发给你一个 sign,你用相同的原材料(参数、密钥、顺序)按照对方的方法做一道菜(计算哈希),如果味道(哈希值)一模一样,就说明菜没被别人动过,对方身份也正确。