在現代 Web 開發中,API 安全驗證是不可或缺的一環。HMAC-SHA1 作為一種廣泛採用的簽名算法,通過雜湊訊息認證碼(HMAC)結合 SHA-1 雜湊函數,確保資料在傳輸過程中的完整性與真實性。本文將詳細解析其核心原理,並提供一個健壯的 PHP 實現範例,涵蓋內建函數與手動實現兩種方案,幫助開發者在不同環境下靈活應用。
HMAC(Hash-based Message Authentication Code)利用對稱密鑰對訊息進行雜湊運算。SHA-1 則是生成 160 位(20 字節)雜湊值的演算法。兩者結合的過程可簡述為:
ipad 與 opad。hash(key ⊕ ipad || message)。hash(key ⊕ opad || inner_hash),最終結果即為簽名。最終輸出通常經 Base64 編碼以便於網路傳輸。
現代 PHP(≥ 5.1.2)內建 hash_hmac() 函數,效能與安全性均優於手動實現。然而,舊版環境或禁用特定擴展的伺服器仍需備用方案。以下函數自動偵測並選擇最優路徑:
<?php
/**
* 使用 HMAC-SHA1 算法生成 OAuth 簽名值
*
* @param string $str 待簽名的源字串
* @param string $key 密鑰
* @return string Base64 編碼後的簽名值
*/
function getSignature($str, $key) {
$signature = '';
// 優先使用內建函數(高效、安全、抗時序攻擊)
if (function_exists('hash_hmac')) {
// raw_output = true 取得原始二進位資料,再 Base64 編碼
$signature = base64_encode(hash_hmac('sha1', $str, $key, true));
} else {
// 手動實現 HMAC-SHA1(相容舊版 PHP)
$blocksize = 64;
$hashfunc = 'sha1';
// 步驟 1:密鑰預處理
if (strlen($key) > $blocksize) {
$key = pack('H*', $hashfunc($key));
}
$key = str_pad($key, $blocksize, chr(0x00));
// 步驟 2:生成填充常數
$ipad = str_repeat(chr(0x36), $blocksize);
$opad = str_repeat(chr(0x5c), $blocksize);
// 步驟 3:巢狀雜湊運算
$innerHash = pack('H*', $hashfunc(($key ^ $ipad) . $str));
$hmac = pack('H*', $hashfunc(($key ^ $opad) . $innerHash));
$signature = base64_encode($hmac);
}
return $signature;
}
?>
pack('H*', ...) 與 chr()?SHA-1 函數回傳十六進位字串,pack('H*') 將其轉為二進位資料,符合 HMAC 規範對位元運算的要求。chr(0x36) 與 chr(0x5c) 則對應 RFC 2104 定義的固定填充位元模式。
hash_hmac() 以固定時間執行;手動實現若用於高安全場景,建議引入 hash_equals() 驗證簽名。X-Hub-Signature 標頭,伺服端以相同密鑰重算比對。| 現象 | 可能原因 | 解決方向 |
|---|---|---|
| 簽名驗證失敗 | 字串編碼不一致(UTF-8 vs 原始位元組) | 確保雙端對源字串採用相同字元集與排序規則 |
| 結果長度異常 | 未開啟 raw_output 或 Base64 編碼錯誤 | 檢查 hash_hmac(..., true) 並確認 base64_encode |
| 舊環境報錯 | hash 擴展未載入 | 啟用 extension=hash 或改用手動實現區塊 |
HMAC-SHA1 雖非最新標準,但在存量系統與特定協議中仍佔主導。掌握其底層運作機制,並能在 PHP 中編寫兼容性良好的實現代碼,是後端工程師處理介面安全的基本功。建議新專案優先採用 HMAC-SHA256,並善用語言內建的加密原語,減少手工造輪子帶來的風險。