请求的签名和核实
请求签名证明发送者的真实性和内容的完整性。与TLS(保护通道)不同,应用程序签名使每个消息都可以验证并抵抗代理,缓存和延迟交付。
目标是:1.真实(发送者)和完整性(未更改)。
2.不重复性(反弹保护)。
3.脱离传输(在HTTP,队列,webhook之上运行)。
4.可审计性(几个月后可复制)。
1)威胁模型(最低)
沿途的身体/头部替换。
Replay(合法请求的重播)。
标题标题的Downgrade/strip。
窃取集成的秘密。
非同步时钟(clock skew)和长队列。
2)选择原语
HMAC(对称性):简单快捷,密钥存储在双方。适用于B2B webhook和内部API。
RSA/ECDSA(不对称性):收件人私有密钥,收件人公开密钥。适用于开放式集成以及不共享秘密的重要性。
mTLS:在传输层进行相互身份验证;通常与NMAS/身体签名结合使用。
JWT/JWS:适用于啤酒代币和自给自足的烙印;对于主体签名,最好使用+JWS Detached/HTTP消息签名。
HTTP Message Signatures(查询选定部分的签名):REST的现代方法。
建议:对于webhooks-HMAC+timestamp+nonce+身体规范化;对于公共API-HTTP消息信号或JWS;高风险-添加mTLS。
3)规范化(我们确切的签名)
签名需要确定性字符串,双方都可以平等地恢复。
参考阵容:
method \n path_with_query_normalized \n content-type \n digest: SHA-256=BASE64(SHA256(body)) \n x-ts: <unix iso> \n x-nonce: <uuid> \n host \n x-tenant: <tenant_id> \n
最后一行:
canonical = join("\n", fields)
signature = HMAC(secret, canonical) # или ECDSA_sign(private_key, canonical)
规则:
- 规范化查询参数的路径和顺序。
- 空格/unicode/寄存器-捕获(例如,下箱头,trim)。
- 大身体-散列(文章)而不包括"原样"。
4)标题格式
HMAC的示例:
X-Signature-Alg: hmac-sha256
X-Signature: v1=hex(hmac),ts=1730379005,nonce=550e8400-e29b-41d4-a716-446655440000,kid=prov_42
Digest: SHA-256=BASE64(SHA256(body))
X-Tenant: brand_eu
不对称示例(ECDSA P-256):
Signature: keyId="prov_42", alg="ecdsa-p256-sha256",
ts="2025-10-31T12:30:05Z", nonce="550e...", headers="(request-target) host digest x-tenant",
sig="BASE64(raw_signature)"
其中"kid"/"keyId"允许从注册表中选择密钥(请参阅轮换)。
5)接受方验证
伪代码:python def verify(request):
1) Basic assert abs (now () - request. ts) <= ALLOWED_SKEW # напр., 300 с assert not replayed(request. nonce, window = TTL) # store nonce/ts in KV
2) Restore canonical canonical = build_canonical (
method=request. method,
path=normalize_path(request. path, request. query),
content_type=request. headers["content-type"],
digest=hash_body(request. body),
ts=request. ts,
nonce=request. nonce,
host=request. headers["host"],
tenant=request. headers. get("x-tenant")
)
3) Get the key key = key_registry. get(request. kid) # secret (HMAC) или public key (ECDSA)
4) Verify if request signature. alg. startswith("hmac"):
ok = hmac_compare(key. secret, canonical, request. signature)
else:
ok = asym_verify(key. public, canonical, request. signature)
5) Solution if not ok: return 401, "SIGNATURE_INVALID"
return 200, "OK"
恒定时间比较HMAC,在快速KV(TTL event_id交付窗口)中存储"nonce"/"( ts,≥)"。
6)反倒带和窗户
Timestamp+Nonce:拒绝此窗口中"± Δ"(例如5分钟)和nonce重播的请求。
对于webhook:使用稳定的"event_id"和inbox表比nonce更可靠。
重新交付(retrai)必须使用相同的ts/nonce/event_id而不是生成新的。
7)多特南特和地区
保存per tenant/region: 'kid=<tenant>键:<region> <key_id>。
共享秘密池和限制;遵守数据驻留。
在标题/规范化中,指定"X-Tenant"和区域是可验证上下文的一部分。
8)密钥管理和轮换
密钥注册表(KMS/Vault):"kid",类型,算法,状态("active","deprecating","retired"),"valid_from/valid_to"。
双秘密:同时保持当前密钥和下一个密钥(接收器同时接受两者)。
按时间表和事件轮换(损害)。
按键(如果可能)并限制对密钥材料的访问。
密钥访问和操作日志。
9)与mTLS和OAuth结合
mTLS在证书级别检查通道和"您是谁"。
签名可保护消息(通过代理/keshi/队列有用)。
OAuth/JWT补充了身份验证/授权,但本身并不能保证身体的完整性(除非在规范化中签名)。
最佳实践:mTLS+身体签名(文摘)+HMAC/ECDSA+简短的'ts' interval。
10)错误和响应代码
'401 SIGNATURE_INVALID'是错误的签名/算法。
'401 KEY_REVOKED'-'kid'无效/过期。
'400 TIMESTAMP_OUT_OF_RANGE'-时钟/窗口。
'409 NONCE_REPLAYED'-检测到重复。
'400 DIGEST_MISMATCH'-身体发生了变化。
'415 UNSUPPORTED_ALGORITHM'是未解决的'alg'。
'429 TOO_MANY_ATTEMPTS' 是按键/tenant进行转弯。
在机器可读的"error_code"中踢出确切的原因;不要像现在这样返回秘密/规范化。
11)可观察性和审计
度量标准:- `verify_p95_ms`, `verify_error_rate`, `digest_mismatch_rate`, `replay_blocked_rate`, `alg_usage{hmac,ecdsa}`, `clock_skew_ms`.
- Logi(结构):"kid","alg","tenant","region","ts","nonce","digest_hash","decision","reason"。
- Tracing:"签名"属性。kid`, `signature.alg`, `signature.ts_skew`.
- 审计:不变轮换日志、密钥使用和公差标志。
12)生产力
通过流媒体哈希身体(不要保持内存)。
通过"kid"缓存公共密钥,并在事件中使用短TTL和残疾。
在edge/gateway上进行预检查(ts/nonce/格式)。
HMAC比ECDSA快;ECDSA更适合于外部集成和"不可分离"密钥。
13)测试
Fixtur集:相同的查询→相同的规范化/签名;"肮脏"的空白/查询/标题顺序→可持续。
Negative:不正确的"kid/alg",修改后的身体/主机,重播nonce,过时的ts,clock skew。
基于属性的:任何等效的查询都会产生一个canonical字符串。
Interop:跨语言检查(Go/Java/Node/Python)。
混乱:延迟,撤退,"即时"更改密钥。
14)花花公子(runbooks)
1."SIGNATURE_INVALID"爆发'
检查密钥轮换、时钟旋转、发送者对规范化的更改。
暂时为旧的"孩子"启用"双接受",通知合作伙伴。
2."REPLAYED"的成长'
增加TTL存储,在发件人处检查转发器,检查时钟.
在边缘阻止滥用IP/ASN。
3. "DIGEST_MISMATCH"大量
检查代理/压缩/标头覆盖;记录规范化版本。
禁用破坏身体/标题的中介。
4.对钥匙的损害
立即恢复"kid",翻译为"next_kid",重新生成所有秘密/令牌,访问审核。
15)典型错误
在不固定顺序的情况下签署"身体部分"或JSON →字段排列的漏洞。
缺乏"文摘"→代理可以谨慎地改变身体。
没有无声的长窗口"ts" →通过下拉打开。
在没有KMS/Vault的环境变量中存储秘密。
比较签名不是稳定时间。
忽略规范化→转发攻击中的"主机"/"路径"。
混合不同的Tenant和地区的"kid"。
16)售前支票清单
- 定义了规范化格式(方法,path +query, content-type, Digest, ts, nonce, host, tenant)。
- 由HMAC/ECDSA使用"kid",密钥注册表和双秘密实现。
- 包括Webhook的反放电(nonce+ts)和inbox/event_id存储。
- 设置了错误代码/retrais策略和trottling per tenant/key。
- 可观察性:verify,logi,traces,alerta on flock的度量。
- 钥匙旋转是自动的;审计和访问权受到限制。
- 规范化和跨语言兼容性测试套件。
- 集成商文档,带有3-4种语言和fixtures的示例。
- mTLS包含在敏感集成中;JWT仅用作补充,不是身体签名的替代品。
结论
请求的签名和验证不是"单个标题",而是纪律:清晰的规范化,短的时间窗口,反反反射,按键旋转和可观察性。为所有集成(API和webhook)构建单一标准,使用"kid"/KMS,在旋转时接受两个密钥,并且您的轮廓将具有抗欺骗性、可预测性和易于审核性。