×

api开发 电商平台

深入淘宝API签名算法:HMAC-SHA256 实现细节与防重放攻击策略

admin admin 发表于2025-08-01 17:01:44 浏览7 评论0

抢沙发发表评论

HMAC(Hash - based Message Authentication Code)即基于哈希的消息认证码,它结合了哈希函数的单向性与密钥的保密性。SHA256 则是安全哈希算法家族中的一员,输出 256 位哈希值,以其高安全性和广泛应用而著称。HMAC - SHA256 通过将密钥与消息数据混合,多次运用 SHA256 哈希函数,生成一个固定长度的唯一标识,用于验证消息在传输过程中未被篡改且来源可靠。
在 Python 中,使hmachashlib库可轻松实现 HMAC - SHA256 计算。示例代码如下:
import hmac
import hashlib
def calculate_hmac_sha256(key, message):
   h = hmac.new(key.encode('utf - 8'), message.encode('utf - 8'), hashlib.sha256)
   return h.hexdigest()
上述代码定义了一个函calculate_hmac_sha256,接收密key和消message作为参数,利hmackey初始化 HMAC 对象,message进行 SHA256 哈希计算,最终返回十六进制格式的哈希值。
二、淘宝 API 签名生成流程
(一)参数准备与排序
淘宝 API 要求将所有请求参数(包括公共参数与业务参数,但排sign参数本身)按照参数名的 ASCII 码升序排列。例如,对于请求参数{"product_id": "123", "page": 1, "timestamp": "1690000000"},排序后变为{"page": 1, "product_id": "123", "timestamp": "1690000000"}。这一步骤确保了无论参数在原始请求中如何排列,生成的签名字符串都是一致的,避免因参数顺序不同导致签名差异。
(二)字符串拼接
将排序后的参数及其值按key1=value1&key2=value2&...的格式拼接成一个字符串,并且需对特殊字符进行 URL 编码。若上述参数拼接后的字符串page=1&product_id=123&timestamp=1690000000。此外,还需在拼接字符串前添加 API 名称,假设 API 名称为/api/taobao/product/query,则最终待签名的基础字符串为/api/taobao/product/querypage=1&product_id=123&timestamp=1690000000
(三)签名计算
使用分配给应用App Secret作为密钥,对上一步生成的基础字符串进行 HMAC - SHA256 加密。在 Python 中实现如下:
app_secret = "your_app_secret"
base_string = "/api/taobao/product/querypage=1&product_id=123&timestamp=1690000000"
sign = calculate_hmac_sha256(app_secret, base_string)
print(sign)
运行这段代码,即可得到符合淘宝 API 要求的签名字符串,该字符串将被附加到请求中,用于服务端验证请求的合法性。
三、防重放攻击策略
(一)时间戳机制
在每个 API 请求中,客户端需添加当前时间的时间戳(通常为 Unix 时间戳,以秒为单位)。服务端接收到请求后,立即检查时间戳的有效性。设定一个合理的时间窗口,如 5 分钟,若请求中的时间戳与服务端当前时间相差超过这个时间窗口,则判定该请求为过期请求,予以拒绝。这一机制有效防止攻击者截获旧请求并在一段时间后重放,因为过期的请求不再被认可。
在 Python 中,服务端验证时间戳的示例代码如下:
import time
def validate_timestamp(timestamp, tolerance=300):  # tolerance为时间窗口,单位秒
   current_time = int(time.time())
   return abs(current_time - timestamp) <= tolerance
validate_timestamp函数,传入请求中的时间戳和允许的时间偏差(此处为 300 秒,即 5 分钟),函数将返TrueFalse表示时间戳是否有效。
(二)随机数(Nonce)策略
客户端每次发起请求时,生成一个唯一的随机数(Nonce),并将其包含在请求参数中。服务端维护一个已使用过的 Nonce 缓存(如使用 Redis),当收到新请求时,首先检查该请求中的 Nonce 是否已存在于缓存中。若存在,则判定为重放攻击,拒绝请求;若不存在,则将该 Nonce 存入缓存,并设置一个略大于时间窗口的过期时间(如 6 分钟),以确保在时间窗口内该 Nonce 不会被重复使用。
以下是使用 Python 和 Redis 实现 Nonce 验证的示例代码:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
def validate_nonce(nonce):
   if r.exists(nonce):
       return False
   r.setex(nonce, 360, "used")  # 设Nonce缓存,有效360
   return True
validate_nonce函数用于验证传入的 Nonce 是否已被使用,若未使用则将其存入 Redis 缓存并返True,否则返False
(三)结合时间戳与 Nonce
将时间戳和 Nonce 结合使用,形成双重防护。时间戳确保请求的时效性,Nonce 保证每个请求在同一时间范围内的唯一性。攻击者即使在时间窗口内截获请求,由于每次请求的 Nonce 不同,也无法通过重放旧请求绕过验证。服务端在验证时,先检查时间戳的有效性,再验证 Nonce 的唯一性,只有当两者都通过时,才继续处理请求的业务逻辑。


群贤毕至

访客