해시 값의 문자열 표현으로부터 해시 방식을 판정하는 방법 - 길이·문자 집합·접두사로 후보를 좁히는 실무 절차

· · 해시, 보안, 비밀번호, 기존 자산 활용, 기술 조사

로그나 DB에 남아 있는 5f4dcc3b5aa765d61d8327deb882cf99$2b$12$... 같은 문자열을 보고 「이것은 무슨 해시인가」를 판정하고 싶은 장면은 꽤 있습니다. 기존 시스템의 이관, 인증 방식 조사, 로그 분석, 타사 시스템 연계에서 여기서 막히는 일은 드물지 않습니다.

다만 여기서 위험한 것은 길이만으로 즉단하는 것입니다.
64자의 16진 문자열을 보고 「SHA-256이네요」라고 단언하는 것은 너무 이릅니다. SHA3-256, SHA-512/256, BLAKE2s-256, BLAKE3의 기본 32-byte 출력도 같은 길이가 될 수 있습니다. 반대로 $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 plain SHA-256이 아님 $5$rounds=5000$N3v8Kx2Lq9Rt$uOTla5GAHaRH2aHlUSjkrZUBCuFiahQZ36O/seB39r3
$6$... sha512crypt plain 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 계일 가능성이 높고, 길이보다 먼저 prefix를 보는 편이 빠릅니다.

2.2 단순한 16진 / Base64의 길이로 후보를 좁히는 표

이것은 prefix가 없는 「그냥 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-byte 출력 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라는 password hash 문자열 $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. 실무에서의 판정 절차

여기서부터는 실제로 어떻게 볼지를 순서대로 정리합니다.
추천 순서는 prefix → 구분자 → 문자 집합 → 길이 → 문맥입니다.

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) 계의 password hash를 의심합니다.

  • {SHA} / {SSHA} / {MD5} / {SMD5}
    LDAP / Apache 계의 표현을 의심합니다.

  • {bcrypt} / {pbkdf2} / {scrypt}
    Spring Security 같은 구현 레이블 붙음 저장 형식을 의심합니다.

여기서의 요령은 「알고리즘 본체」뿐만 아니라 「저장 형식」도 보는 것입니다.
예를 들어 $6$는 「SHA-512의 digest」가 아니라 「SHA-512를 쓴 password hash 문자열」입니다. 여기를 잘못 잡으면 그 후의 조사가 어긋납니다.

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 / 44, 86 / 88 같은 「양쪽 다 가능한 길이」를 가집니다.

crypt 계의 radix64

./가 나오고, 게다가 $...$로 구분되어 있다면 보통의 Base64보다 crypt 계의 alphabet을 의심하는 편이 자연스럽습니다.
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의 password hash 형식을 의심

  • .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-byte 출력을 가지는 방식은 여럿 있습니다. SHA3-256, SHA-512/256, BLAKE2s-256, BLAKE3의 기본 출력 등도 같은 길이입니다.

길이는 후보군을 만드는 재료이지, 확정 재료가 아닙니다.

4.2 $6$를 plain SHA-512로 오해한다

$6$...는 sha512crypt의 prefix입니다.
이것은 「SHA-512의 hex digest」가 아니라 salt나 rounds를 포함하는 password hash 문자열입니다.

마찬가지로,

  • $5$는 sha256crypt
  • $1$는 md5crypt

입니다.
prefix가 붙어 있는 시점에서, 이미 「그냥 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 문자열

이 셋은 보임새가 비슷해도 다루는 방식이 다릅니다.
특히 password hash는 salt, rounds, memory cost, parallelism 등을 문자열에 포함하는 경우가 많고, 「생 digest를 비교한다」는 발상으로는 간파할 수 없습니다.

4.5 XOF나 가변 길이 digest를 잊는다

SHAKE128 / SHAKE256은 XOF이므로 출력 길이를 자유롭게 선택할 수 있습니다.
BLAKE2도 digest length를 바꿀 수 있고, BLAKE3도 extendable output을 가집니다.

즉, 「이 길이이니까 이 방식」이라는 추정은 고정 길이의 고전적 digest를 너무 전제로 하면 벗어납니다.

5. 100% 확정하고 싶을 때의 확인 순서

이관이나 인증 연계에서는 최종적으로 확정이 필요합니다. 그때는 다음 순서로 보면 사고가 나기 어렵습니다.

5.1 저장원을 특정한다

먼저 어디서 온 문자열인지를 확정합니다.

  • Linux의 shadow인가
  • Apache / Nginx의 basic auth인가
  • LDAP인가
  • Django / Spring Security인가
  • 자체 앱의 DB인가

문자열 단독보다 저장원의 사양 쪽이 강한 경우가 많습니다.

5.2 공식 문서에서 「저장 형식」을 조사한다

다음으로 알고리즘명이 아니라 저장 형식을 조사합니다.

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

처럼 format / storage / encoding을 키워드로 하면 찾기 쉽습니다.

5.3 알려진 평문이 있다면, 후보 방식으로 실제로 대조한다

테스트용 계정이나 알려진 평문이 있다면, 후보 방식으로 실제로 계산해 비교하는 것이 빠릅니다.
이때 password hash에서는 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) 계의 prefix가 붙은 형식

이렇게 해 두면 나중에 본 사람이 덜 헤맵니다.
반대로 「그냥 64 hex」만을 DB에 두는 설계는 미래의 자신에게 친절하지 않습니다.

6. 정리

해시 값의 문자열 표현으로부터 방식을 구별할 때는 다음 순서로 보면 정리하기 쉽습니다.

  1. prefix가 있는가
  2. 구분 문자가 무엇인가
  3. 문자 집합이 무엇인가
  4. 길이가 몇 바이트에 해당하는가
  5. 저장원의 문맥이 무엇인가

가장 중요한 것은 다음 2가지입니다.

  • 접두사 붙은 저장 형식은 꽤 특정하기 쉽다
  • 단순한 16진 / Base64는 후보군까지밖에 모르는 경우가 많다

따라서 실무에서의 판단은 이렇게 됩니다.

  • $argon2id$..., $2b$..., $6$..., {SHA}..., pbkdf2_sha256$...이라면 문자열만으로 꽤 앞으로 나아갈 수 있다
  • 32 / 40 / 64 / 128자의 16진뿐이라면 단정이 아니라 「후보를 좁힌다」라고 생각한다
  • 정말로 확정이 필요하다면 저장원의 제품·설정·구현까지 본다

이 순서로 보면 조사가 꽤 빨라집니다.
반대로 길이만으로 즉단하면, 조용히 돌아서 갑니다.

7. 이 주제와 이어지는 서비스

기술 상담·설계 리뷰

기존 DB에 남은 비밀번호 해시의 방식 특정, 인증 기반의 이관, Windows / Web 혼재 시스템의 로그 조사에서는, 문자열의 겉모습뿐만 아니라 저장원의 구현이나 이관 방침까지 정리할 필요가 있습니다. 방식 특정부터 이관 설계까지 한꺼번에 보면 사고를 줄이기 쉽습니다.

불량 조사·원인 분석

「이 문자열이 무엇인지 모르겠어서 검증이 진행되지 않는다」는 조사는 드물지 않습니다. 로그, 설정 파일, DB 스키마, 앱 구현의 어디에서 방식이 결정되고 있는지를 구분하면 원인 특정이 꽤 빨라집니다.

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

관련 기사

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

관련 토픽

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 주제와 연결되는 서비스

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

블로그 목록으로 돌아가기