请求签名(Request Signing)是指每次调用接口时,调用方用一把只有它和服务端知道的密钥(Secret Key),对这次请求的内容算出一串短字符串,也就是签名,并把它附在请求里;服务端用同样方式重算一遍,只有结果对得上才接受请求。HMAC(Hash-based Message Authentication Code,基于哈希的消息认证码)是其中最常用的算法之一。
光有 API Key 为什么不够
API Key 像一张工牌:门卫看到工牌就放行,可一旦工牌被人拍照、打印进日志或被复制,别人拿着也能进门。它只能回答“你声称自己是谁”,回答不了另外三件事:
- 请求内容在路上有没有被改动,比如收款账号、调用额度、提示词里的指令;
- 这串请求是不是别人抓包后原样重发的(重放攻击,replay attack);
- 请求是不是刚发出的,而不是三天前的旧包。
签名把这些问题一起解决:请求内容变一个字节,签名就变,这保证了完整性;没有密钥,就算不出正确签名,这实现了身份认证;再配合时间戳(timestamp)和一次性随机数(nonce),服务端只接受时间窗口内、nonce 尚未用过的请求,从而防止重放。
HMAC 是怎么算的
可以把 HMAC 想成“带暗号的封条”。寄快递前,你和收件人约定一个只有彼此知道的暗号;你把箱子里每样东西按固定顺序报一遍,用暗号算出校验码写在封条上。收件人开箱前照同样顺序再算一遍,只要对不上就拒收,因为箱子在路上被换过。
工程上的做法通常是:把 HTTP 方法、路径、排好序的查询参数、时间戳、nonce 和请求体摘要,按文档规定的顺序拼成一个“规范化字符串”,再用 Secret Key 对它做 HMAC(常见如 HMAC-SHA256),最后把结果放进请求头。服务端重算并比对,同时检查时间戳是否在允许窗口内、nonce 是否已经用过。HMAC 不可逆,拿到签名也推不出密钥。
最常见的一个坑是:Secret Key 只能放在服务端。浏览器、App 或客户端代码里的密钥等于公开,谁都能提取出来。另外,签名不负责保密,请求报文仍是明文,如果需要保密,仍然要用 TLS。
| 机制 | 主要解决什么 | 带密钥 | 能发现内容被改 | 防重放 |
|---|---|---|---|---|
| API Key | 表明调用方身份 | 是(长期凭证) | 否 | 否 |
| TLS | 保护传输通道 | 用证书 | 通道内可以 | 否 |
| 普通哈希 | 生成内容指纹 | 否 | 能,但谁都能重算 | 否 |
| HMAC 签名 + 时间戳 + nonce | 来源真实、内容未改、请求新鲜 | 是 | 是 | 是 |
对从业者和普通人的意义
做 AI 应用时,第三方模型服务的异步回调、支付与计费通知、Webhook 几乎都要验签:不验签,任何人都能往你的回调地址推假事件,可能触发扣费或业务动作。自己设计服务间调用时,内网、低风险的读接口用 API Key 就够了;一旦涉及钱、额度、敏感写操作,或者要跨越不可信网络、经过第三方代理,就该用签名。调试签名失败时,问题十有八九出在“规范化”不一致:参数顺序、URL 编码、请求体被框架重新序列化、多了一个空格。具体算法、请求头字段名和允许的时间窗口各家不同,以官方文档为准。
普通人其实天天在用类似思路:短信验证码一次性有效,作用类似 nonce;银行卡交易带报文认证,改了金额就无法通过。
