请求签名怎么验证?

wen python案例 2

本文目录导读:

请求签名怎么验证?

  1. 第一部分:准备工作
  2. 第二部分:通用验证步骤(核心流程)
  3. 第三部分:不同场景的验证细节
  4. 总结与关键提醒

验证请求签名通常用于确保数据在传输过程中未被篡改,并验证发起方身份的真实性,最常见的场景是API接口签名(如微信支付、阿里云API)。

验证签名的基本思路是:使用与签名方相同的算法、密钥和参数顺序,重新计算一次签名,然后将计算出的签名与请求携带的签名进行比对,如果一致,则验证通过。

下面是一个通用的、标准的验证流程,主要分为“准备工作”和“核心计算”两部分。

第一部分:准备工作

在开始验证前,你需要拿到以下东西:

  1. 请求数据:请求体(Body)或请求中的参数。
  2. 签名值:请求中携带的签名,通常在请求头 X-Sign 或某个特定参数 sign 中。
  3. 签名算法:对方使用的算法,如 MD5SHA1SHA256(最常用),或 HMAC-SHA256
  4. 签名密钥这是最关键的一点。 验证方必须持有与签名方相同的密钥,这个密钥通常是在双方协商时约定好的(如 AppSecret)。
  5. 参数排序规则:签名方和验证方必须使用完全相同的参数顺序进行拼接。

第二部分:通用验证步骤(核心流程)

假设我们用的是最常见的 HMAC-SHA256SHA256 签名方式(发送 POST 请求,参数包含 timestamp, nonce, data)。

第1步:提取签名值

从请求头或请求参数中拿到对方提供的签名。 received_sign = "abc123def..."

第2步:获取签名原始字符串(最重要的一步)

你需要把所有需要参与签名的参数(包括时间戳、随机数、请求体内容等)按照特定的顺序拼成一个字符串。

常见规则:

  • 排序:通常按参数名字典序(ASCII码升序)排序。
  • 拼接:将 key=value& 连接起来。
  • 特殊处理:有些场景会拼接 API 路径,或直接将整个请求体 JSON 字符串作为原始字符串。

示例: 假设参数有 timestamp=1718000000, nonce=abc123, data={"user":"1"}

  1. 按字典序排序后:data, nonce, timestamp
  2. 拼接字符串:data={"user":"1"}&nonce=abc123×tamp=1718000000
  3. (可选)有些规则会在前面或后面拼接 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)中验证签名! 密钥会暴露。

微信/支付宝回调(典型例子)

微信支付的回调验证方式是:

  1. 先读取 HTTP 头部的 Wechatpay-Signature
  2. Wechatpay-TimestampWechatpay-Nonce、请求体 Body 拼在一起。
  3. 使用微信平台证书里的公钥(非对称加密)进行验签。
  4. 验证通过后,还需要解密敏感数据。

核心区别:微信使用的是非对称加密(RSA),而不是共享密钥,验证方拿到的是公钥,只能用来验证,无法伪造签名。

总结与关键提醒

检查项 说明
密钥安全 密钥(Secret)储存在你的后端配置文件中永远不要硬编码在前端或暴露给用户。
排序一致 签名方的排序规则(如字典序)必须与验证方完全一致,否则验签必败。
空值与编码 注意对参数值进行 URLEncode(常见于 HTTP GET 请求)或保持原始字符串(常见于 Body),规则要统一。
时间复杂度 使用 HMAC 比直接拼接密钥要安全得多,建议优先选择。
重放攻击 验签通过后,还要检查时间戳和 Nonce(一次性随机数),防止别人记录下你的请求报文反复调用。

一句话流程:对方发给你一个 sign,你用相同的原材料(参数、密钥、顺序)按照对方的方法做一道菜(计算哈希),如果味道(哈希值)一模一样,就说明菜没被别人动过,对方身份也正确。

抱歉,评论功能暂时关闭!