本文面向想在PHP 中理解並正確應用哈希函數的開發者。我們將對比MD5 和SHA-1,從原理、輸出、抗碰撞性、速度、典型用途以及在PHP 中的實際用法給出清晰、務實的建議,並說明更安全的替代方案與使用注意事項。
哈希(散列)函數:接受任意長度輸入,輸出固定長度的“摘要”。常用於校驗數據完整性、生成簽名基礎、做索引等。
不可逆性:哈希函數應是單向的——理論上不能從摘要反推出原始輸入(但並非絕對,取決於攻擊者能力)。
碰撞:不同輸入產生相同輸出的情況。理想哈希應難以找到碰撞。
輸出長度
MD5:128 位,通常以32 字符十六進製表示(如d41d8cd98f00b204e9800998ecf8427e )。
SHA-1:160 位,通常以40 字符十六進製表示(如da39a3ee5e6b4b0d3255bfef95601890afd80709 )。
安全性(摘要)
MD5:已被證明存在實際可行的碰撞攻擊,不能用於安全敏感場景(簽名、證書、密碼存儲等)。
SHA-1:比MD5 強,但也已被實證破解(研究/實驗證明可找到碰撞),已被逐步棄用作為安全哈希。
速度
MD5 通常比SHA-1 更快(更短的內部狀態與運算量更少),但速度差異在現代硬件上不是主要考量——安全性更重要。
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$data</span></span><span> = </span><span><span class="hljs-string">"hello world"</span></span><span>;
</span><span><span class="hljs-comment">// MD5</span></span><span>
</span><span><span class="hljs-variable">$md5</span></span><span> = </span><span><span class="hljs-title function_ invoke__">md5</span></span><span>(</span><span><span class="hljs-variable">$data</span></span><span>); </span><span><span class="hljs-comment">// 32 hex</span></span><span>
</span><span><span class="hljs-variable">$md5_raw</span></span><span> = </span><span><span class="hljs-title function_ invoke__">md5</span></span><span>(</span><span><span class="hljs-variable">$data</span></span><span>, </span><span><span class="hljs-literal">true</span></span><span>); </span><span><span class="hljs-comment">// 原始二進制 16 位元組</span></span><span>
</span><span><span class="hljs-comment">// SHA-1</span></span><span>
</span><span><span class="hljs-variable">$sha1</span></span><span> = </span><span><span class="hljs-title function_ invoke__">sha1</span></span><span>(</span><span><span class="hljs-variable">$data</span></span><span>); </span><span><span class="hljs-comment">// 40 hex</span></span><span>
</span><span><span class="hljs-variable">$sha1_raw</span></span><span> = </span><span><span class="hljs-title function_ invoke__">sha1</span></span><span>(</span><span><span class="hljs-variable">$data</span></span><span>, </span><span><span class="hljs-literal">true</span></span><span>); </span><span><span class="hljs-comment">// 原始二進制 20 位元組</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"MD5: <span class="hljs-subst">$md5</span></span></span><span>\n";
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"SHA1: <span class="hljs-subst">$sha1</span></span></span><span>\n";
</span></span>對於需要用密鑰的消息認證(更安全的完整性校驗),請使用HMAC:
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$key</span></span><span> = </span><span><span class="hljs-string">"secret-key"</span></span><span>;
</span><span><span class="hljs-variable">$data</span></span><span> = </span><span><span class="hljs-string">"important payload"</span></span><span>;
</span><span><span class="hljs-comment">// 推薦使用更長更安全的哈希(示例:sha256)</span></span><span>
</span><span><span class="hljs-variable">$hmac</span></span><span> = </span><span><span class="hljs-title function_ invoke__">hash_hmac</span></span><span>(</span><span><span class="hljs-string">'sha256'</span></span><span>, </span><span><span class="hljs-variable">$data</span></span><span>, </span><span><span class="hljs-variable">$key</span></span><span>);
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"HMAC-SHA256: <span class="hljs-subst">$hmac</span></span></span><span>\n";
</span></span>僅做非安全性唯一標識/ 快速校驗(小風險可接受)
MD5 或SHA-1 都可用於生成文件名哈希、簡單的緩存鍵、去重的快速判斷(但不能依賴為強一致性保證)。
用於安全敏感場景(絕對不推薦)
密碼存儲、數字簽名、TLS/證書指紋、簽名驗證等:不要使用MD5/ SHA-1 。兩者都存在實用的碰撞攻擊(尤其MD5),攻擊者可偽造相同摘要的不同消息。
文件完整性校驗
若只是防止傳輸時意外損壞(非對抗者場景),MD5/SHA-1 仍常見於文件下載頁。但若擔心惡意篡改,請用更強的哈希(SHA-256/512)或簽名(HMAC/公私鑰簽名)。
MD5 :自2000 年代早期以來出現多種碰撞攻擊和實用攻擊,已被廣泛棄用用於安全用途。
SHA-1 :長期被認為比MD5 更安全,但研究與實驗證明存在實用碰撞(例如學術界在2017 年公佈的實用碰撞演示),因此在安全性要求高的場合也應避免使用。
(備註:以上為概述性說明,已公開的研究和工實踐表明兩者在抵抗碰撞方面不足。)
密碼永遠使用專門的密碼散列函數:使用password_hash() / password_verify() ,PHP 默認( PASSWORD_DEFAULT )會使用現代安全算法(如bcrypt 或更好的實現),並自動處理隨機鹽與成本因子。
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-comment">// 密碼哈希與驗證(示例)</span></span><span>
</span><span><span class="hljs-variable">$password</span></span><span> = </span><span><span class="hljs-string">'user-password'</span></span><span>;
</span><span><span class="hljs-variable">$hash</span></span><span> = </span><span><span class="hljs-title function_ invoke__">password_hash</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>, PASSWORD_DEFAULT);
</span><span><span class="hljs-comment">// 存儲 $hash 到數據庫</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">password_verify</span></span><span>(</span><span><span class="hljs-variable">$password</span></span><span>, </span><span><span class="hljs-variable">$hash</span></span><span>)) {
</span><span><span class="hljs-comment">// 驗證通過</span></span><span>
}
</span></span>消息完整性與認證用HMAC 或簽名:對對抗環境請使用hash_hmac() (例如用sha256或sha512 ),或使用公鑰簽名(openssl_sign 等):
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$tag</span></span><span> = </span><span><span class="hljs-title function_ invoke__">hash_hmac</span></span><span>(</span><span><span class="hljs-string">'sha256'</span></span><span>, </span><span><span class="hljs-variable">$message</span></span><span>, </span><span><span class="hljs-variable">$secret_key</span></span><span>);
</span></span>一般哈希首選SHA-2 或SHA-3 系列:例如sha256 、 sha512 ,在PHP 中可通過hash('sha256', $data)或hash('sha512', $data)使用;這些在強度上顯著好於MD5/SHA-1。
若需要對二進制更高效處理,可使用原始二進制輸出( md5($data, true) / sha1($data, true) ),並對結果做base64 編碼存儲或傳輸。
不要試圖“用鹽+多次哈希”自己造密碼哈希方案:容易出錯、難以維護。請使用語言/框架推薦的密碼散列API。
輸出長度:MD5 = 128 位(32 hex),SHA-1 = 160 位(40 hex)。
安全性:SHA-1 > MD5,但兩者都不再適合安全關鍵場景。
速度:MD5 略快,但差異不應成為選用標準(安全性優先)。
推薦用途:僅用於非安全的唯一標識或兼容場景;安全用途請用SHA-2/SHA-3/HMAC/password_hash。
如果你在寫新代碼並且關心安全性,不要使用MD5 或SHA-1 —— 用hash('sha256', $data) / hash_hmac('sha256', $data, $key)做完整性與認證,用password_hash()管理密碼;只有在兼容歷史系統或對安全無關的場景下才考慮MD5/SHA-1。