详解API签名认证。SecretToken、SecretId+SecretKey、非对称加密,你都区分清楚了吗?

352 阅读5分钟

1. API签名认证

API签名认证是一种用于保护Web API的身份验证机制。它用于验证发送API请求的客户端或用户的身份,并确保请求数据在传输过程中没有被篡改。API签名认证通常涉及以下步骤:

  1. 请求创建: 客户端创建API请求,并包括必要的请求参数、数据和请求头
  2. 签名生成: 客户端使用密钥和一些请求信息(如时间戳等)来生成一个唯一的数字签名sign。签名生成通常使用哈希算法(如HMAC-SHA256)
  3. 签名附加: 客户端将生成的数字签名附加到API请求中,通常是在请求头或请求参数中
  4. 服务器验证: 服务端收到API请求后,会使用相同的密钥和接收到的请求信息来生成签名。然后,它将生成的签名与请求中的签名sign进行比较。

⚠️如果生成签名的过程中没有使用某个请求数据,那么对该请求数据的篡改将是不可知的

在日常使用软件中,我们可能会注意到有两种API认证方式:

  • 方式一:只有一个密钥

    在注册SM.MS图床后,网站会提供一个Secret Token(可以随时查看),把Secret Token填写到PicGo这类软件中后,就可以方便地将图片上传到SM.MS

    image-20250412164038994

  • 方式二:有两个密钥

    在注册腾讯云后,网站会提供给我们一对密钥,分别叫SecretId和SecretKey,且SecretKey只能在创建时告诉用户一次,之后无法查看,只能重新创建。有了这两个密钥,我们就可以通过ddns-go动态修改所购买域名的域名解析。

    image-20250412164357645

这两种认证方式是如何工作的?有什么区别?


1.1 单密钥

网站给你一个 SecretToken,你在软件中配置这个 Token,它会被加到 HTTP 请求头中:

POST /upload
Authorization: Bearer sk_123456789abcdef

在这种场景下,SecretToken就是用户的身份标识。但是和JWT还不同,SecretToken中并不包含状态信息(Payload),它只是一个静态的、用户唯一的标识。当网站接受到你的请求时,他会在自己的数据库中查询有没有这个Token并确定你是谁。

单密钥的整个过程不涉及加密/签名,轻量简单。


1.2 SecretId+SecretKey

在这种场景下,SecretId 相当于你的用户名,SecretKey 则是你的密码,用于签名(不直接参与网络传输)。每次请求都要用这两个参数进行签名计算,类似这样:

POST /ddns/update
Action: UpdateDomainRecord
Timestamp: 1682341234
SecretId: sid_123456789
Signature: sha256_hmac(secretKey, 所有参数)

{
	"domain": "a.com",
	"ip": "1.2.3.4"
}

当服务端接收到这个请求时,首先根据SecretId去数据库中查询对应的SecretKey,然后对请求参数(Timestamp、SecretId、domain,ip)进行同样的签名操作,然后与Signature进行比较。总的来说,SecretId+SecretKey的方案依然是一种对称加密(哈希消息认证码,HMAC)

防篡改

签名计算的目的是为了防止请求参数的篡改。例如,你的请求被某个中间人截获,如果他修改某个请求参数(他无法修改Signature),那么服务端进行签名操作得到的结果就会和Signature不同,进而拒绝请求。

防重放
  1. Timestamp

    携带Timestamp是为了防止重放攻击。想象某个中间人截获了你的转账请求,他不进行数据篡改,而是反复重放该请求,那么也会造成攻击效果。为此可以将请求的时间戳作为参数的一部分,也进行签名(这意味着攻击者无法篡改时间戳),后端在执行业务逻辑之前首先判断该时间戳是否在范围之内。

  2. nonce(随机串)+缓存机制

    单纯携带Timestamp并不能解决“短时重放攻击”的问题,为此,可以再加上一招:nonce(随机串)+缓存机制。客户端请求时,加入 timestamp + nonce 参数,服务端校验时,判断 timestamp 在时间窗口内(如1分钟),且在该时间窗口内 nonce 是否首次出现。如果 nonce 在时间窗口内已经出现过一次,直接拒绝请求,否则将 nonce 加入缓存(如 Redis),设置过期时间 1分钟

    接收到请求
        ↓
    检查 timestamp 是否过期
        ↓
    检查 nonce 是否在缓存中
        ├─ 是 → 拒绝(重放)
        └─ 否 → 验证签名 → 缓存 nonce(5分钟) → 通过
    

1.3 非对称加密

上面提到了SecretId+SecretKey的方案本质是一种对称加密,那么为什么需要非对称加密,以及它是如何工作的?

分析SecretId+SecretKey的工作流程,客户端需要使用SecretKey对请求数据进行签名,尽管普通的业务不会发生SecretKey的传输,但用户获取自己SecretKey的过程依然会发生SecretKey的传输(这也是为什么腾讯云只给你看一次SecretKey,再想查看只能重建)

以HTTPS来说明非对称加密的流程:

  1. 服务器把公钥发给客户端
  2. 客户端生成一个对称加密用的 AES 密钥
  3. 客户端用服务器公钥加密 AES 密钥,发回给服务器
  4. 服务器用私钥解密得到 AES 密钥
  5. 后续通信用 AES(对称加密)进行加密传输

非对称加密的目的是为了传输一个对称加密通信使用的密钥AES(相当于上面的SecretKey,但避免了直接传输),一旦完成了AES的传输,后续的通信是通过对称加密实现的。用公钥加密的数据只有对应的私钥才能解密,客户端从始至终都不知道私钥,也从未发生AES密钥的直接传输。