請求的簽名和核實
請求簽名證明發送者的真實性和內容的完整性。與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,在旋轉時接受兩個密鑰,並且您的輪廓將具有抗欺騙性、可預測性和易於審核性。