MD5 vs SHA-256, 어떤 해시 알고리즘을 써야 할까?
해싱은 컴퓨팅에서 가장 기본적인 연산 중 하나이지만, 잘못된 알고리즘을 고르면 시스템이 충돌 공격, 데이터 무결성 훼손, 불필요한 성능 오버헤드에 노출될 수 있습니다. 이 가이드에서는 가장 널리 쓰이는 네 가지 해시 알고리즘을 비교하고, 명확한 선택 기준을 제시합니다.
해시 함수란?
암호학적 해시 함수는 임의의 입력 데이터를 받아 고정된 크기의 출력(즉 “다이제스트” 또는 “해시”)을 만들어 냅니다. 좋은 해시 함수는 다음 세 가지 속성을 갖춰야 합니다.
- 결정론적: 같은 입력은 언제나 같은 출력을 만들어 냅니다.
- 일방향성: 해시 값을 되돌려 원본 입력을 복원할 수 없습니다.
- 충돌 저항성: 동일한 해시를 만들어 내는 서로 다른 두 입력을 찾는 것이 계산적으로 실행 불가능해야 합니다.
세 번째 속성이 무너지면 해당 알고리즘은 “암호학적으로 깨진(cryptographically broken)” 상태로 간주됩니다. 실제로 MD5와 SHA-1에서 벌어진 일이 바로 이것입니다.
알고리즘 한눈에 비교
| 항목 | MD5 | SHA-1 | SHA-256 | SHA-512 |
|---|---|---|---|---|
| 출력 크기 | 128 비트 (16진수 32자) | 160 비트 (16진수 40자) | 256 비트 (16진수 64자) | 512 비트 (16진수 128자) |
| 블록 크기 | 512 비트 | 512 비트 | 512 비트 | 1024 비트 |
| 공개 연도 | 1991년 | 1995년 | 2001년 | 2001년 |
| 설계자 | Ron Rivest | NSA / NIST | NSA / NIST | NSA / NIST |
| 충돌 저항성 | 깨짐 (2004년) | 깨짐 (2017년) | 안전 | 안전 |
| 상대 속도 | 가장 빠름 | 빠름 | 보통 | 보통 (64비트에서 더 빠름) |
| NIST 지위 | 권장 중단 | 권장 중단 | 권장 | 권장 |
MD5: 빠르지만 깨진 알고리즘
MD5(Message-Digest Algorithm 5)는 1991년 Ronald Rivest가 설계했으며, 1990년대와 2000년대 초반에 걸쳐 체크섬의 사실상 표준으로 자리 잡았습니다. 128 비트 해시를 16진수 32자로 출력합니다.
MD5가 깨진 이유
2004년 Xiaoyun Wang이 MD5에 대한 실용적인 충돌 공격을 시연했습니다. 2008년에는 연구자들이 MD5 충돌을 이용해 위조 SSL 인증서를 만들어 내면서, 이 공격이 단순한 이론에 머물지 않는다는 사실이 증명되었습니다. 오늘날 MD5 충돌은 일반 소비자용 하드웨어에서 몇 초 만에 생성할 수 있습니다.
// MD5 해시 생성 (보안 용도가 아닌 경우에만 사용)
// Web Crypto API는 MD5를 지원하지 않으므로 라이브러리를 사용합니다
import { md5 } from 'hash-wasm';
const hash = await md5('Hello, World!');
console.log(hash);
// → 'bea8252ff4e80f41719ea13cdf007273' (16진수 32자)
MD5를 아직 써도 괜찮은 경우
암호학적으로 깨졌음에도, MD5는 보안과 무관한 용도에서는 여전히 유용합니다.
- 파일 중복 제거: 스토리지 시스템에서 동일한 파일을 찾아내기
- 캐시 키: 캐시 조회용으로 짧고 결정론적인 키를 생성하기
- 데이터 무결성 체크섬: 데이터가 우연히 손상되지 않았는지 빠르게 확인하기(의도적 조작과는 무관)
- 레거시 호환: MD5를 요구하는 구형 시스템과 상호 운용하기
핵심은 이렇게 구분됩니다. MD5는 우연한 손상은 잡아내지만, 의도적인 조작은 막지 못합니다.
SHA-1: 권장 중단되었지만 여전히 남아 있는
SHA-1(Secure Hash Algorithm 1)은 NSA가 설계하고 1995년 NIST가 발표했습니다. 160 비트 해시를 16진수 40자로 출력합니다.
2017년 Google과 CWI Amsterdam은 최초의 실용적인 SHA-1 충돌(이른바 “SHAttered” 공격)을 시연하며, 같은 SHA-1 해시를 가지는 서로 다른 두 PDF 파일을 만들어 냈습니다. 주요 브라우저와 인증 기관은 이미 2016년부터 SHA-1 인증서를 거부하기 시작한 상태였습니다.
SHA-1은 해당 알고리즘을 요구하는 시스템과의 호환 목적으로만 사용하시기 바랍니다. 예를 들어 Git은 커밋 해시에 SHA-1을 쓰지만 SHA-256으로 전환 중입니다. 새로 개발하는 프로젝트라면 SHA-256 이상을 선택하십시오.
SHA-256: 현재의 표준
SHA-256은 SHA-2 계열의 일부로, NSA가 설계하고 2001년 NIST가 발표했습니다. 256 비트 해시를 16진수 64자로 출력하며, 2026년 현재 거의 모든 해싱 용도에 대해 권장되는 알고리즘입니다.
// Web Crypto API로 SHA-256 해시 생성 (브라우저 내장)
async function sha256(text) {
const data = new TextEncoder().encode(text);
const hash = await crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
const hash = await sha256('Hello, World!');
console.log(hash);
// → 'dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f'
SHA-256이 기본 선택인 이유
- 알려진 공격 없음: 2026년 현재 SHA-256에 대한 실용적인 충돌/역상 공격은 존재하지 않습니다
- 브라우저 내장: 모든 최신 브라우저가 Web Crypto API로 지원하므로 별도 라이브러리가 필요 없습니다
- 업계 표준: TLS 인증서, Bitcoin, 패키지 매니저(npm, pip), Docker 이미지 다이제스트, 대부분의 무결성 검증 시스템이 SHA-256을 사용합니다
- NIST 승인: 보안이 중요한 모든 애플리케이션에 권장됩니다
SHA-512: 더 강한 여유가 필요할 때
SHA-512는 512 비트 해시를 16진수 128자로 출력합니다. 블록 크기가 1024 비트(SHA-256은 512 비트)이기 때문에, 64비트 프로세서에서는 사이클당 더 많은 데이터를 처리하여 SHA-256보다 빠릅니다.
# Python: SHA-512도 SHA-256만큼 쉽습니다
import hashlib
hash_256 = hashlib.sha256(b'Hello, World!').hexdigest()
hash_512 = hashlib.sha512(b'Hello, World!').hexdigest()
print(f"SHA-256: {hash_256}") # 64자
print(f"SHA-512: {hash_512}") # 128자
SHA-512는 다음과 같은 경우에 사용합니다.
- 더 큰 보안 여유가 필요할 때 (SHA-256의 128 비트 충돌 저항성 대비 256 비트)
- 64비트 플랫폼에서 성능이 중요할 때 (x86-64에서 SHA-512가 SHA-256보다 1.5배 빠를 수 있음)
- 프로토콜이나 규격이 요구할 때 (일부 인증서 체계, 특정 컴플라이언스 요구 사항)
대부분의 경우에는 SHA-256으로 충분합니다. SHA-512는 해시 길이가 두 배로 늘어나 저장 공간과 대역폭을 그만큼 더 차지하지만, 일반적인 용도에서는 실질적인 보안 이점이 없습니다.
의사 결정 프레임워크: 어떤 해시를 쓸 것인가
파일 무결성과 체크섬
SHA-256을 사용합니다. 다운로드 검증, 파일 내용 비교, 데이터가 손상되지 않았는지 확인하는 표준 알고리즘입니다. 기존 MD5 기반 시스템을 교체한다면 SHA-256이 가장 자연스러운 드롭인 업그레이드입니다.
# 다운로드 파일의 무결성 검증
sha256sum downloaded-file.tar.gz
# 출력값을 공개된 해시와 비교
비밀번호 저장
MD5도, SHA-256도 그대로 쓰지 마십시오. 범용 해시 함수는 비밀번호 해싱에는 너무 빠릅니다. 공격자가 초당 수십억 번의 추측을 시도할 수 있기 때문입니다. 대신 비밀번호 해싱 전용 알고리즘을 사용하십시오.
| 알고리즘 | 상태 | 비고 |
|---|---|---|
| Argon2id | 권장 | 2015년 Password Hashing Competition 우승자, 메모리 하드 |
| bcrypt | 양호 | 폭넓은 지원, 내장 salt, 조절 가능한 작업 계수 |
| scrypt | 양호 | 메모리 하드, 일부 암호화폐 시스템에서 사용 |
| PBKDF2 | 허용 가능 | NIST 승인이지만 메모리 하드는 아님, ≥ 600,000회 반복 권장 |
| SHA-256 | 부적합 | 너무 빠르고 내장 salt가 없으며, GPU 공격에 취약 |
| MD5 | 위험 | 깨졌고 너무 빠름, 손쉽게 복원됨 |
// 잘못된 예: 비밀번호를 SHA-256으로 해싱하지 마십시오
const hash = await sha256(password); // 초당 수십억 회로 돌파 가능
// 올바른 예: bcrypt 사용 (Node.js)
import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 12); // 12 rounds ≈ 250ms
const isValid = await bcrypt.compare(input, hash);
HMAC과 메시지 인증
SHA-256과 HMAC을 함께 사용합니다. HMAC(Hash-based Message Authentication Code)은 해시 함수와 비밀 키를 결합하여 무결성과 진위성을 동시에 검증합니다.
// 웹훅 서명 검증용 HMAC-SHA256
async function verifyWebhook(payload, signature, secret) {
const key = await crypto.subtle.importKey(
'raw', new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' }, false, ['verify']
);
const sig = Uint8Array.from(atob(signature), c => c.charCodeAt(0));
return crypto.subtle.verify('HMAC', key, sig, new TextEncoder().encode(payload));
}
콘텐츠 주소 지정 스토리지
SHA-256을 사용합니다. Git, Docker, IPFS는 모두 콘텐츠의 해시를 그 주소로 삼는 콘텐츠 주소 지정(content-addressable) 스토리지를 사용합니다. SHA-256은 수십억 개의 객체 사이에서도 유일성을 보장할 만큼 충분한 충돌 저항성을 갖추고 있습니다.
성능 벤치마크
최신 x86-64 프로세서에서의 상대적 해싱 속도(높을수록 빠름)는 다음과 같습니다.
| 알고리즘 | 처리량 (MB/s) | 상대 속도 |
|---|---|---|
| MD5 | ~3,200 | 1.0배 (기준) |
| SHA-1 | ~2,400 | 0.75배 |
| SHA-256 | ~1,500 | 0.47배 |
| SHA-512 | ~2,100 | 0.66배 |
참고: SHA-512는 내부 상태가 더 넓기 때문에 64비트 프로세서에서는 SHA-256보다 빠릅니다. 32비트 시스템에서는 반대가 됩니다. 대부분의 용도에서는 속도 차이가 거의 의미가 없습니다. 1 MB 파일을 해싱할 때 어느 알고리즘을 써도 1 ms 이내에 끝납니다.
흔히 저지르는 실수
1. 보안 용도로 MD5를 사용
MD5 충돌은 손쉽게 만들어 낼 수 있습니다. 디지털 서명, 인증서 검증, 공격자가 악의적인 입력을 설계할 수 있는 모든 상황에서 MD5를 절대 사용하지 마십시오.
2. 비밀번호를 SHA-256으로 해싱
SHA-256은 비밀번호 해시가 아닙니다. 너무 빠르고, 내장 salt가 없으며, 레인보우 테이블(Rainbow Table)과 GPU 가속 무차별 대입 공격에 취약합니다. Argon2id 또는 bcrypt를 사용하시기 바랍니다. 무차별 대입 저항성의 수학적 배경이 궁금하다면 비밀번호 엔트로피 개념을 찾아보시기 바랍니다.
3. 해시 길이를 보안 강도와 동일시
512 비트 해시가 256 비트 해시보다 자동으로 “더 안전한” 것은 아닙니다. 충돌 저항성 관점에서 SHA-256의 128 비트 보안 여유는 이미 무차별 대입 한계를 한참 벗어납니다. 실제 요구 사항에 맞춰 선택하시고, 단순한 해시 길이로 판단하지 마시기 바랍니다.
4. 직접 조합한 해시 구성
SHA256(MD5(data))나 MD5(data + salt) 같은 패턴은 깨진 알고리즘을 마법처럼 고쳐 주지 않습니다. 충분히 검증된 단일 알고리즘(SHA-256)이나 올바른 구성(HMAC)을 사용하십시오.
직접 시도해 보기
Hash Generator에서 MD5, SHA-1, SHA-256, SHA-512 해시를 즉시 생성하고 비교해 볼 수 있습니다. 텍스트를 붙여 넣거나 파일을 올리면 네 가지 다이제스트를 한 화면에서 나란히 확인할 수 있습니다. 모든 처리가 브라우저 안에서 이루어지며, 데이터는 기기를 벗어나지 않습니다.
인증, 입력 유효성 검사, 보안 헤더를 비롯한 더 넓은 웹 보안 주제와, 인코딩·해싱·데이터 변환 전반의 개발자 도구 라인업은 홈페이지 카테고리에서 함께 살펴볼 수 있습니다.
자주 묻는 질문
MD5는 체크섬 용도로 여전히 안전한가요?
MD5는 파일이 우연히 손상되었는지 감지하는 용도로는 안전합니다. 다운로드 중 파일이 훼손되면 MD5 해시도 거의 확실히 달라지기 때문입니다. 다만 공격자가 같은 MD5 해시를 가지는 변조 파일을 만들어 낼 수 있으므로, 의도적 조작에는 안전하지 않습니다. 소프트웨어 배포처럼 악의적 공격자를 고려해야 하는 무결성 검증에는 SHA-256을 사용하시기 바랍니다.
SHA-256은 MD5보다 얼마나 느린가요?
순수 처리량 기준으로 SHA-256은 MD5보다 약 2배 느립니다. 최신 하드웨어에서 MD5는 약 3,200 MB/s, SHA-256은 약 1,500 MB/s 수준으로 해싱합니다. 대부분의 애플리케이션에서는 이 차이가 무시할 만합니다. 예를 들어 100 MB 파일을 SHA-256으로 해싱해도 약 70 ms면 끝납니다. 성능 격차가 실제로 문제 되는 경우는 네트워크 패킷 검사나 대규모 스토리지 중복 제거처럼 매우 높은 처리량이 필요한 시나리오뿐입니다.
제 애플리케이션에 SHA-256과 SHA-512 중 어느 쪽을 써야 하나요?
대부분의 경우 SHA-256을 사용하시기 바랍니다. 128 비트의 충돌 저항성을 갖추고 있으며, 이는 이미 무차별 대입 한계를 한참 넘어서는 수준입니다. SHA-512는 다음 조건에서 선택하시기 바랍니다. (a) 플랫폼이 64비트이며 최대 처리량이 필요한 경우, (b) 규격이나 규정 준수가 명시적으로 요구하는 경우, (c) 장기 데이터 무결성을 위해 더 큰 보안 여유가 필요한 경우. SHA-512의 추가 해시 길이는 저장 공간을 두 배로 늘리며, 대체로 필요하지 않습니다.
SHA-256은 복호화하거나 역산할 수 있나요?
아닙니다. SHA-256은 일방향 함수이므로 역산할 수 없습니다. 2026년 현재 SHA-256에 대한 실용적인 역상 공격이나 충돌 공격은 존재하지 않습니다. 알려진 최선의 충돌 공격조차 2^128 회의 연산이 필요한데, 이는 현재는 물론 가까운 미래의 기술로도 계산 불가능한 수준입니다. SHA-256은 NIST의 승인을 받았으며, TLS, Bitcoin, 연방 정부 시스템 등 핵심 인프라에서 사용됩니다.
MD5가 깨졌는데도 아직 쓰는 시스템이 있는 이유는 무엇인가요?
가장 큰 이유는 레거시 호환입니다. 많은 기존 시스템과 프로토콜, 파일 포맷은 MD5의 취약점이 드러나기 전에 MD5를 중심으로 설계되었습니다. 여기서 벗어나려면 모든 구성 요소에서 조율된 변경이 필요합니다. 또한 캐시 키나 중복 제거처럼 보안과 무관한 용도에서는, 충돌 공격이 위협 모델의 관심사가 아니기 때문에 MD5의 빠른 속도와 짧은 출력이 실용적인 선택으로 남기도 합니다.