從雜湊值的字串表示判斷雜湊方式 - 以長度、字元集、前綴縮小候選的實務步驟

· · 雜湊, 資安, 密碼, 既有資產活用, 技術調查

經常會遇到以下情境:在日誌或資料庫中看到 5f4dcc3b5aa765d61d8327deb882cf99$2b$12$... 這類字串,想判斷「這是什麼雜湊」。既有系統遷移、認證方式調查、日誌分析、跨系統整合時,常常就在這一步卡住。

不過這時的危險是 只憑長度立刻下定論
看到 64 字元的 16 進位字串就說「是 SHA-256」其實太早。SHA3-256、SHA-512/256、BLAKE2s-256、BLAKE3 的預設 32 位元組輸出也都是同一長度。反過來,像 $2b$$argon2id$ 這類 連前綴與參數都包含的保存格式,光看字串就能相當準確地判斷。

本文把 hash 一詞用得較廣,不只涵蓋 MD5 / SHA-2 / SHA-3 這類訊息摘要,也包含 bcrypt / scrypt / Argon2 / PBKDF2 等 用於密碼保存的字串表示
內容是以 2026 年 4 月時點公開的 RFC、NIST、Linux crypt(5)、Apache、Django、Spring Security 等官方資料為根據整理而成。

1. 先下結論

先簡短整理一下結論:

  • 帶有前綴或分隔符的保存格式,光看字串就相當容易特定。
    例:$argon2id$..., $2b$..., $5$..., $6$..., {SHA}..., pbkdf2_sha256$...

  • 只看純粹的 16 進位字串或 Base64,大多只能「縮小候選」。
    例:32 hex = 可能是 MD5,但也可能是 MD4 / NTLM 系列

  • 字元集的重要性和長度一樣高。
    + / = 很可能是 RFC 4648 的 Base64;含 . 且以 $ 分隔則可能是 crypt(3) 系列。

  • 想 100% 確定就需要上下文。
    來源是 /etc/shadow.htpasswd、Django 的 auth_user,還是 Spring Security,結論會差很多。

也就是說,「光憑字串就可以特定的方式」「光憑字串只能鎖定到候選群的方式」 是兩回事。
先把兩者分開來看,調查會快很多。

2. 一眼判斷表

2.1 可以靠前綴或格式標記幾乎特定的情況

這邊的「判斷強度」含義如下:

  • :光看字串就幾乎能特定
  • :候選可以明顯縮小,但要注意實作差異
  • :只看長度或外觀無法下定論
外觀特徵 首先懷疑的方式 判斷強度 補充
$argon2id$... Argon2id PHC string format。後面多半接 v=, m=, t=, p= $argon2id$v=19$m=65536,t=3,p=4$MDEyMzQ1Njc4OWFiY2RlZg$uKZLaN6muIyoyIYr5waqw3y+zaDbe9aLSPj6Ln/rbz4
$argon2i$... Argon2i 同上 $argon2i$v=19$m=65536,t=3,p=4$MDEyMzQ1Njc4OWFiY2RlZg$Kx1koF/7n8EytGJYTS5krh+ag+FlG5ksM4xOsjOSDvo
$argon2d$... Argon2d 同上 $argon2d$v=19$m=65536,t=3,p=4$MDEyMzQ1Njc4OWFiY2RlZg$HLIGA+T1bwK8akx3LGOco+Df+PvxX6cIXhycO7O7t6c
$2a$... / $2b$... / $2y$... bcrypt 2 位 cost + crypt 系 alphabet $2b$12$9YQ2u/e5Y/ArOnG.gJKxK.0makLATcYLP1q.Nsabzrw7XErYCfoYO
$1$... md5crypt Unix 系以 MD5 為基礎的密碼保存格式 $1$vA7mQ9xZ$Erz32JUFnZ9991KdU5.N3.
$5$... sha256crypt 不是純粹的 SHA-256 $5$rounds=5000$N3v8Kx2Lq9Rt$uOTla5GAHaRH2aHlUSjkrZUBCuFiahQZ36O/seB39r3
$6$... sha512crypt 不是純粹的 SHA-512 $6$rounds=5000$N3v8Kx2Lq9Rt$6LUcSUAELX3aC/.60pTB.TFLTQi1mOGRCwKqNCqtRSaXjorxj01HJ9oNni97Kci1uDt7a/Kn4t3OS20Dw/.vi1
$7$... scrypt(crypt 系) Linux crypt(5) 系實作中會出現 $7$CU..../....k2XAnEHBqQ1Ct2aMXFKNa/$y3Q0e/UlCHacIGWQshgvvz6UIbP.BCja.5BfVWP2Ml8
$y$... yescrypt 較新的 Linux 系會用到 $y$j9T$k2XAnEHBqQ1Ct2aMXFKNa/$OVYXzjlkiQpWT/F1CUE0JrvV4phLY8FB.ofDttnrSQ7
$apr1$... Apache APR1-MD5 .htpasswd 常見 $apr1$vA7mQ9xZ$ZE64.ohiyK11sPZmtnJZQ.
{SHA}... SHA-1 digest 的 Base64 表示 Apache / LDAP 系常見 {SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=
{SSHA}... salted SHA-1 LDAP 系 {SSHA}/OczD0GNNkOAUPbYhA3L9fjmcyBCbHVlTWVzYTQyIQ==
{MD5}... / {SMD5}... MD5 / salted MD5 LDAP 系 {MD5}X03MO1qnZdYdgyfeuILPmQ==
{SMD5}fOn1rOv4ZH0OrO/KT9H0fEJsdWVNZXNhNDIh
pbkdf2_sha256$... PBKDF2-HMAC-SHA256 中~強 如 Django,實作會把格式名前置 pbkdf2_sha256$600000$N3v8Kx2Lq9Rt$CLxGB+zTiV1IdOt2y4m9JpaAONzHuRTOd96xKQwRQAs
{bcrypt}$2b$... bcrypt Spring Security 的 {id} 包裝 {bcrypt}$2b$12$9YQ2u/e5Y/ArOnG.gJKxK.0makLATcYLP1q.Nsabzrw7XErYCfoYO
{pbkdf2}... / {scrypt}... 帶實作標籤的方式 中~強 如 Spring Security,先看包裝格式而不是單看底層演算法 {pbkdf2}sha256$600000$Qmx1ZU1lc2E0MiE$4eNuai1qNkgs1kXz3+tBUMzAexVsSUz9SrQKEhbk0Cw
{scrypt}ln=14,r=8,p=1$Qmx1ZU1lc2E0MiE$xAgBRhXbMtHB1UHUR0br5bI+1XdXWKbwauiFv5VRQBY

這張表的重點是,字串開頭少數幾個字元就能帶出意義的格式,判斷起來很強
尤其是用 $...$ 分隔的格式,很可能是 Unix crypt(3) / MCF / PHC 系,看前綴比看長度來得快

2.2 純 16 進位 / Base64 依長度縮小候選的表

下面這張表是看 沒有前綴的「純粹 digest 字串」 時用的。
若字串中混有 :-、空白等分隔符,要先去掉再算長度。

原始位元組 16 進位字元數 Base64 字元數(含 / 不含 = 主要候選
4 8 8 / 6 CRC32 等檢查碼 cbf43926
16 32 24 / 22 MD5、MD4、NTLM 系 5f4dcc3b5aa765d61d8327deb882cf99
20 40 28 / 27 SHA-1、RIPEMD-160 da39a3ee5e6b4b0d3255bfef95601890afd80709
28 56 40 / 38 SHA-224、SHA-512/224、SHA3-224 d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f
32 64 44 / 43 SHA-256、SHA-512/256、SHA3-256、BLAKE2s-256、BLAKE3 的預設 32 位元組輸出 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
48 96 64 / 64 SHA-384、SHA3-384、BLAKE2b-384 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b
64 128 88 / 86 SHA-512、SHA3-512、BLAKE2b-512、Whirlpool cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e

這邊關鍵是:長度一致,方式還是無法唯一決定
尤其 32 / 64 / 128 位的 16 進位字串,候選相當多,只看長度很容易判斷錯。

2.3 容易誤判的代表例

字串外觀 常見的立刻下結論 實際的看法
5f4dcc3b5aa765d61d8327deb882cf99 一定是 MD5 看起來像 MD5,也可能是 MD4 / NTLM 系或應用自行套用的 MD5 8846f7eaee8fb117ad06bdd830b7586c
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 這類 64 hex 一定是 SHA-256 是 SHA-256 的候選,但也可能是 SHA3-256 / SHA-512/256 / BLAKE2s-256 / BLAKE3 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
$6$rounds=5000$salt$hash SHA-512 的 hex 表示 不是,是 sha512crypt 的密碼雜湊字串 $6$rounds=5000$N3v8Kx2Lq9Rt$6LUcSUAELX3aC/.60pTB.TFLTQi1mOGRCwKqNCqtRSaXjorxj01HJ9oNni97Kci1uDt7a/Kn4t3OS20Dw/.vi1
{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE= 某種「SHA」 在 Apache / LDAP 系脈絡中,多半指 Base64 編碼後的 SHA-1 digest {SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=
{bcrypt}$2b$12$... {bcrypt} 這個獨立方式 其實是 Spring Security 加了包裝的 bcrypt {bcrypt}$2b$12$9YQ2u/e5Y/ArOnG.gJKxK.0makLATcYLP1q.Nsabzrw7XErYCfoYO

3. 實務判定步驟

下面把實際要怎麼看,按順序整理起來。
建議順序:前綴 → 分隔符 → 字元集 → 長度 → 上下文

3.1 先看開頭的符號

光是前面 1~10 字,就能把範圍縮小很多。

  • $argon2id$ / $argon2i$ / $argon2d$
    強烈懷疑是 Argon2 的 PHC string format。組成元素可以對照 2.1 的「例」欄。

  • $2a$ / $2b$ / $2y$
    強烈懷疑是 bcrypt。

  • $1$ / $5$ / $6$ / $7$ / $y$
    Unix crypt(3) 系的密碼雜湊格式。

  • {SHA} / {SSHA} / {MD5} / {SMD5}
    LDAP / Apache 系的表示法。

  • {bcrypt} / {pbkdf2} / {scrypt}
    類似 Spring Security 的 帶實作標籤的保存格式

這邊的訣竅是:不只要看「底層演算法」,也要看「保存格式」
比如 $6$ 不是「SHA-512 的 digest」,而是「以 SHA-512 為基礎的密碼雜湊字串」。把這一點弄混後面的調查就會偏掉。

3.2 數一下分隔符

接著看 $:{},= 這類分隔符。

  • $ 出現多次
    很可能是把參數、salt、hash 一起帶在字串裡的格式。Argon2、bcrypt、sha256crypt、sha512crypt 都是典型。

  • {name} 開頭
    很可能是 LDAP / Spring Security 等 明示演算法名稱的包裝

  • algo:salt:hashalgo$iterations$salt$hash 的形式
    通常是框架或應用自己定義的格式。Django 的 pbkdf2_sha256$iterations$salt$hash 就是典型。

分隔符越多的字串,越容易特定方式。
反過來,如果只是一整塊的 16 進位或 Base64,就會變得相當模糊。

3.3 看字元集

字元集的重要性和長度一樣。

16 進位表示

只含 [0-9a-fA-F] 的話,先視為 16 進位表示。
這時 字元數 ÷ 2 = 原始位元組數

  • 32 hex → 16 bytes
  • 40 hex → 20 bytes
  • 64 hex → 32 bytes
  • 128 hex → 64 bytes

RFC 4648 的 Base64 / Base64url

+ / = 就先視為一般的 Base64;
- _ 則視為 Base64url。
由於 padding 的 = 有時會省略,所以常會看到像 43 / 4486 / 88 這種「兩種長度都可能」的情況。

crypt 系的 radix64

若出現 ./,加上以 $...$ 分隔,比起當成普通 Base64,不如 視為 crypt 系的字母表 更自然。
bcrypt、sha256crypt、sha512crypt、md5crypt、yescrypt、scrypt 等都是使用這種字元集。

這一點雖然不起眼,但很關鍵。
若把有 . 的字串當成壞掉的 Base64,就很容易漏掉 bcrypt 或 crypt(3) 系。

3.4 數長度

字元集搞清楚後,再來看長度。
思路很單純:

  • 若是 16 進位:原始位元組數 = 字元數 / 2
  • 若是 Base64:字元數 ≒ 4 × ceil(原始位元組數 / 3)
    若 padding 的 = 被省略,會少 0~2 個字元

這階段用來縮小候選。
但請注意,不要從「64 hex」直接跳到「確定是 SHA-256」,這種飛躍很危險。

3.5 用上下文決定

最後能一路推到 100% 的,是上下文。

  • 出現在 /etc/shadow
    懷疑是 $y$$6$$5$$1$ 等 Linux 的密碼雜湊格式

  • 出現在 .htpasswd
    懷疑是 $apr1${SHA}、bcrypt 等 Apache 系

  • 出現在 Django 的設定或 auth_user.password
    懷疑是 pbkdf2_sha256$...argon2$... 這種 Django 格式

  • 出現在 Spring Security 的認證表
    懷疑是 {bcrypt}...{pbkdf2}...{id} 前置格式

  • 出現在 SMB / AD 整合相關處,且是 32 hex
    強烈懷疑 NTLM / MD4 系

實務上,只看字串本身,不如直接去看來源產品、框架與設定檔名稱,經常來得更快。

4. 常見的誤判

4.1 一看到 64 hex 就咬定是 SHA-256

這個陷阱很常見。
當然 SHA-256 是強候選,但能產出 32 位元組輸出的方式不只一個:SHA3-256、SHA-512/256、BLAKE2s-256、BLAKE3 的預設輸出長度都一樣。

長度是拿來列出候選群的資訊,不是拿來確定的依據。

4.2 把 $6$ 誤解成純 SHA-512

$6$... 是 sha512crypt 的前綴。
它不是「SHA-512 的 hex digest」,而是 包含 salt 與 rounds 的密碼雜湊字串

同理,

  • $5$ 是 sha256crypt
  • $1$ 是 md5crypt

只要看到這種前綴,就已經不是「純 digest」。

4.3 以為 {SHA} 是「SHA-256 或 SHA-512 其中之一」

在 Apache 或 LDAP 脈絡下,{SHA} 並不是模糊的「某種 SHA」。
多數情況下,它指的是 Base64 編碼後的 SHA-1 digest{SSHA} 則是 salted SHA-1。

只憑 {SHA} 的外觀就含糊地當成「某種 SHA」,驗證程式或遷移處理就會出錯。

4.4 把密碼雜湊與內容雜湊當成同一件事

同樣是「雜湊字串」,但目的可能不同:

  • 用來確認檔案完整性的 digest
  • 用於 API 簽章的 digest
  • 用於密碼保存的 hash / KDF 字串

即使看起來很像,這三者處理方式不同。
尤其密碼雜湊常把 salt、rounds、memory cost、parallelism 等都嵌進字串中,用「比對原始 digest」的思路是看不穿的。

4.5 忘了 XOF 與可變長度 digest

SHAKE128 / SHAKE256 是 XOF,可以任意選擇輸出長度。
BLAKE2 的 digest 長度可調整,BLAKE3 也支援可延伸輸出。

也就是說,「這長度就是這個方式」 的推論,若只站在「固定長度、傳統 digest」的前提上,很容易判斷錯。

5. 想 100% 確定時的檢查順序

做系統遷移或整合驗證時,最後必須要能 確定。這時依下列順序看,比較不會出意外:

5.1 先定位來源

先確定這串字到底是從哪裡來:

  • Linux 的 shadow?
  • Apache / Nginx 的 basic auth?
  • LDAP?
  • Django / Spring Security?
  • 自家應用的資料庫?

有時候,來源系統的規格比字串本身更有說服力

5.2 用官方文件查「保存格式」

接著,不是查演算法名稱,而是查 保存格式

  • Django password format
  • Spring Security password storage format
  • crypt(5) sha512crypt format
  • Apache htpasswd password formats

可以用 format / storage / encoding 當關鍵字去搜,會更好找。

5.3 有已知明文時,實際以候選方式比對

如果手上有測試帳號或已知明文,最快的方式就是用候選方式實際算出雜湊再比對。
此時對密碼雜湊來說,要 從字串中取出 salt 與 rounds 後重新計算

5.4 檢查實作程式或設定

若調查的是自家系統,最終還是看程式碼與設定最確定:

  • 使用的函式庫
  • 框架設定
  • 產生時的選項
  • 輸出編碼方式(hex / Base64 / Base64url / crypt alphabet)

通常看到這層就會有結論。

5.5 未來設計時,用帶格式標籤的方式保存

如果你是未來要做設計的人,採用 把演算法資訊嵌入字串 的格式,能讓後續遷移輕鬆很多:

  • Argon2 的 PHC string format
  • Spring Security 的 {id}encodedPassword
  • Django 的 algo$iterations$salt$hash
  • Unix crypt(3) 系帶前綴的格式

這樣將來接手的人比較不會迷路。
反過來,把「純 64 hex」直接塞到資料庫的設計,對未來的自己不太友善。

6. 結語

想從雜湊值的字串判斷演算法,照下列順序看會比較容易整理:

  1. 有沒有前綴
  2. 有哪些分隔符
  3. 字元集是什麼
  4. 長度大約對應多少位元組
  5. 來源的上下文是什麼

最重要的兩點是:

  • 帶前綴的保存格式,通常很好特定
  • 純粹的 16 進位 / Base64,常常只能鎖到候選群

因此實務判斷會是這樣:

  • $argon2id$...$2b$...$6$...{SHA}...pbkdf2_sha256$...,光看字串就能推進很多
  • 只有 32 / 40 / 64 / 128 位的 16 進位字串時,應該視為「縮小候選」,不是「下結論」
  • 真的要確定時,務必看來源系統、設定、實作程式

依這個順序看,調查會快很多。
反之,光憑長度就下結論,就會在看不見的地方繞遠路。

7. 這個主題相關的服務

技術諮詢、設計審查

在特定既有資料庫中的密碼雜湊演算法、進行認證系統遷移、或在 Windows / Web 混合環境中做日誌調查時,除了看字串外觀,還必須整理來源實作、遷移策略。把「特定演算法」到「遷移設計」一併看,比較能減少事故。

問題調查、原因分析

「因為不知道這個字串是什麼,所以驗證卡住了」這類調查其實不罕見。把「究竟是日誌、設定檔、資料庫結構,還是應用實作在決定演算法」這件事切分清楚後,原因特定會快很多。

8. 參考資料

  1. RFC 1321 - The MD5 Message-Digest Algorithm
  2. NIST FIPS 180-4 - Secure Hash Standard (SHA-1, SHA-2, SHA-512/224, SHA-512/256)
  3. NIST FIPS 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions
  4. PHC string format specification
  5. Argon2 reference implementation
  6. RFC 7693 - The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)
  7. BLAKE3 C README - default output length and extendable output
  8. crypt(5) - prefixes and hashed passphrase formats
  9. Apache HTTP Server 2.4 - Password Formats
  10. slappasswd(8) - RFC 2307 schemes such as {SHA} and {SSHA}
  11. Django documentation - example of pbkdf2_sha256$...
  12. Spring Security - DelegatingPasswordEncoder storage format {id}encodedPassword

相關文章

共用相同標籤的最新文章。能以相近的主題延伸理解。

偽隨機數與真正隨機數的差異 - 如何區分的整理

本文整理偽隨機數與真正的隨機數差異,重點不在輸出外觀而在產生器結構:一般 PRNG 重視可重現性、CSPRNG / DRBG 主打不可預測性、NRBG 則以物理熵源為基礎。文中說明 seed 與 reseed、health test 的角色,並給出資安、模擬、抽籤等用途的選...

閱讀文章

為什麼郵件安全裡 PPAP 行不通?正確的做法是什麼?

本文整理為什麼把附件壓成有密碼的 ZIP 後再用同一條郵件路徑寄出密碼的 PPAP 在當今環境下並不安全,並從竊聽防護、寄錯防護、惡意程式檢查與真實性等角度說明問題,再以 TLS、S/MIME、附帶驗證的下載等實務替代方案,協助讀者重新設計符合目的的郵件與檔案交付控制。

閱讀文章

不被特定服務綁住的中小企業群發郵件設計方式

本文以中小企業為對象,整理在不導入專用 EDM 服務的前提下,如何用既有網域與官網建立小型派送平台:個別寄送、訂閱與抑制名單、自動退訂動線,以及 SPF/DKIM/DMARC 與 PTR、TLS 等寄件者認證的最低要求,幫助讀者在數十至數百封規模長期穩定不出事地持續發信。

閱讀文章

相關主題

與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。

與本主題相關的服務

本文連結到以下服務頁面,歡迎從最接近的入口查看。

回到部落格一覽