HMAC 生成器

使用 SHA-1、SHA-256、SHA-384 或 SHA-512 為任意訊息與金鑰計算 HMAC 簽名,支援 hex、base64、base64url 輸出。

本地計算訊息認證碼。 HMAC 將雜湊函式與金鑰結合,使接收方既能校驗完整性也能驗證真實性。通過 Web Crypto API 在瀏覽器內完成。

HMAC 到底解決什麼問題

它做認證,不做加密

HMAC 不隱藏任何內容。它回答的是另一個問題:這條訊息是否由持有共享金鑰的人生成,以及在此之後是否被改動過?輸出是一個定長標籤,隨訊息一起傳送;接收方用相同金鑰重新計算並比對。標籤一致說明訊息真實且完整;不一致則說明兩者都不成立,而且無法區分究竟是哪一種。

正因如此,HMAC 是 webhook 簽名、API 請求籤名、簽名 Cookie 和會話令牌背後的主力。Stripe、GitHub 以及大多數 webhook 服務商都用這種方式簽名負載,好讓你能驗證請求確實來自它們,而不是來自某個猜中了你介面地址的人。

為什麼不能直接把金鑰和訊息拼起來做雜湊

最直覺的構造 SHA256(金鑰 + 訊息) 在 SHA-1、SHA-256 這類 Merkle-Damgård 雜湊上是不安全的。這類雜湊按塊處理資料並向後傳遞內部狀態,因此攻擊者只要知道一個合法摘要,就能在訊息後追加資料並算出擴充套件訊息的合法摘要,全程無需得知金鑰。這就是長度擴充套件攻擊,它並非紙上談兵,已經攻破過真實的 API。

HMAC 的巢狀構造 H(金鑰 XOR opad || H(金鑰 XOR ipad || 訊息)) 堵住了這個漏洞。兩輪使用不同填充金鑰的運算,使最終摘要不再是"對攻擊者可影響資料做雜湊"後的裸內部狀態。這也是為什麼你應當直接使用 HMAC,而不要自創帶金鑰的雜湊方案。

實務要點

除非外部條件另有約束,選 SHA-256。SHA-1 在 HMAC 內部仍然安全,因為該構造並不依賴抗碰撞性,但新系統沒有理由繼續使用它,而且審計時一定會被標記。SHA-512 在這個用途上並沒有實質更強,只是在 64 位硬體上有時更快。

有兩個實現細節比選哪個雜湊更重要。第一,比較標籤必須使用恆定時間比較——普通字串比較在第一個不同位元組處就提前返回,洩露的時間資訊足以讓攻擊者逐位元組偽造出標籤。第二,金鑰應當是長度不小於摘要長度的隨機位元組,而不是一句好記的口令;HMAC 金鑰不是密碼雜湊,不會經過任何強化處理。

開源說明:使用瀏覽器原生的 Web Crypto(crypto.subtle)API 實現,不依賴第三方庫。

常見問題

我的金鑰會被髮送出去嗎?
不會。金鑰和訊息始終留在瀏覽器內,簽名通過 Web Crypto API 在本地計算,不會傳輸也不會儲存。
應該選擇哪個演算法?
新專案一律用 SHA-256。SHA-384 和 SHA-512 能以很小的代價提高安全裕度;SHA-1 只是為了相容老系統而保留。
base64 和 base64url 有什麼區別?
base64url 用 - 和 _ 替換 + 和 /,並去掉了填充字元,因此結果可以直接放進 URL、檔名和 HTTP 頭。
HMAC 屬於加密嗎?
不屬於。它只能證明訊息出自持有金鑰的一方且未被篡改,訊息內容本身仍然完全可讀。如果還需要保密,請配合 TLS 或加密使用。
為什麼不能直接把金鑰和訊息拼起來做雜湊?
普通 SHA-2 存在長度擴充套件攻擊:攻擊者無需知道金鑰,就能在已簽名的訊息後追加資料並偽造出有效摘要。HMAC 的巢狀結構堵住了這個漏洞。
程式碼裡應該怎麼比較兩個 HMAC?
使用常數時間比較,例如 crypto.timingSafeEqual。普通的 == 會在第一個不同位元組處提前返回,可能讓攻擊者逐位元組試出正確的簽名。