비밀번호 보안의 변화: SHA-256에서 Argon2id까지

SHA-256 — 강력하지만 너무 빠른 해시
SHA-256이란?
SHA-256(Secure Hash Algorithm 256-bit)은 미국 NSA가 설계하고 2001년 NIST가 표준화한 암호학적 해시 함수(cryptographic hash function) 입니다. 어떤 길이의 입력을 받아도 256비트(32바이트) 고정 길이의 출력을 만들어 줍니다.
SHA-256("hello") → 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
SHA-256("hello!") → ce06092fb948d9ffac7d1a376e404b26b7575bcc11ee05a4615fef4fec3a308b핵심 성질은 다음과 같습니다.
결정성: 같은 입력은 항상 같은 출력
눈사태 효과(avalanche effect): 입력이 1비트만 바뀌어도 출력이 완전히 달라짐
단방향성(one-way): 출력에서 입력을 역산하는 게 사실상 불가능
충돌 저항성: 같은 출력을 만드는 서로 다른 두 입력을 찾는 게 사실상 불가능
이 성질 덕분에 SHA-256은 비트코인의 작업 증명, 디지털 서명, 파일 무결성 검증 등 현대 암호학의 핵심 부품으로 쓰입니다.
처음에는 비밀번호 저장에도 썼다
SHA-256이 등장하기 전, 시스템은 비밀번호를 평문으로 저장하거나 MD5/SHA-1 같은 깨진 해시로 저장했습니다. SHA-256이 표준화되자 많은 시스템이 옮겨갔지요.
js
// 2000년대 흔했던 패턴
const hash = sha256(password)
db.users.insert({ email, hash })하지만 곧 문제가 드러났습니다.
문제: SHA-256은 비밀번호 저장용으로 "너무 빠르다"
SHA-256은 데이터 무결성 검증을 위해 설계된 함수입니다. 즉 빠른 게 미덕이에요. 큰 파일의 무결성을 확인할 때, 해시가 빠를수록 좋으니까요.
그런데 이 "빠름"이 비밀번호 저장에서는 치명적인 약점이 됩니다.
일반 CPU: 초당 약 1억 회
고성능 GPU(RTX급): 초당 약 100~500억 회
비트코인용 ASIC: 초당 수십조 회 이상
사람이 만드는 비밀번호의 실제 엔트로피는 보통 20~30비트, 즉 가능한 후보 수는 약 10억 개입니다. GPU 한 대로 1초도 안 걸려 전부 시도할 수 있는 규모이지요.
여기에 레인보우 테이블(rainbow table) 까지 더해지면, 단일 SHA-256은 거의 종이 자물쇠 수준이 됩니다. 공격자가 자주 쓰이는 비밀번호의 해시를 미리 계산해두고 유출된 DB와 대조하면 끝납니다.
SHA-256 자체는 약하지 않습니다. 다만 비밀번호 저장용으로는 "용도가 맞지 않습니다."
이 인식이 다음 세대의 출발점이 되었습니다.
PBKDF2 — "느리게 만들기"라는 발상의 전환
PBKDF2란?
PBKDF2(Password-Based Key Derivation Function 2)는 2000년 RSA Labs가 발표하고 RFC 2898/8018로 표준화된 키 파생 함수(Key Derivation Function, KDF) 입니다.
핵심 발상은 이렇습니다.
해시가 너무 빠른 게 문제라면, 일부러 느리게 만들면 되지 않을까?
PBKDF2는 HMAC-SHA256 같은 의사난수함수를 수십만 번 반복해서 비밀번호를 키로 변환합니다.
DK = HMAC(HMAC(HMAC(... 600,000번 반복 ...)))여기에 사용자마다 다른 솔트(salt) 를 함께 입력으로 넣어, 사전 계산 공격(레인보우 테이블)을 무력화합니다.
핵심 통찰: 비용 비대칭
PBKDF2의 진짜 묘미는 공격자와 정당 사용자의 비용 차이입니다.
작업 | SHA-256 단일 해시 | PBKDF2 (60만 회) |
|---|---|---|
정상 로그인 1회 | 거의 0초 | 0.5초 (사용자 못 느낌) |
후보 1억 개 공격 | 약 5초 | 약 1.5년 |
사용자 비용은 거의 그대로
공격자 비용은 수십만 배로 증가
이게 PBKDF2의 본질입니다. "비밀번호를 강하게 만드는" 게 아니라, "공격자가 약한 비밀번호를 깨기 비싸게 만드는" 거예요.
한계가 드러나다: GPU와 ASIC의 시대
PBKDF2는 한동안 표준이었습니다. 하지만 2010년대 들어 GPU와 ASIC이 보편화되면서 새로운 약점이 노출됩니다.
PBKDF2는 CPU 연산만 요구하고 메모리는 거의 쓰지 않습니다. 이게 무슨 의미일까요?
GPU는 수천 개의 작은 코어를 가지고 있어서 단순 계산을 병렬로 엄청나게 많이 처리할 수 있습니다. 메모리를 거의 쓰지 않는 PBKDF2는 GPU에게 완벽한 먹잇감이었지요. CPU 1개의 처리량은 같아도, GPU 한 대가 수천 개의 비밀번호 후보를 동시에 시도할 수 있게 된 겁니다.
ASIC은 더 심각합니다. 특정 알고리즘만 처리하도록 설계된 전용 칩이라, 일반 컴퓨터로는 상상할 수 없는 효율을 냅니다.
여기서 새로운 질문이 떠오릅니다.
공격자가 병렬 하드웨어를 동원해도 이득을 못 보게 하려면 어떻게 해야 할까?
답은 메모리에 있었습니다.
Memory-Hard — 새로운 차원의 방어
Memory-Hard란?
Memory-Hard 함수는 "계산을 빠르게 하려면 많은 메모리가 필요한" 구조의 함수를 말합니다. 2009년 콜린 퍼시벌(Colin Percival)이 scrypt 논문에서 정식 개념화했지요.
핵심 발상은 이렇습니다.
CPU 계산량만 늘리면 ASIC이 우위를 점한다. 그러면 메모리도 많이 쓰게 만들어, ASIC의 이점을 무력화하자.
왜 메모리가 효과적인가?
병렬 하드웨어의 강점과 약점을 따져보면 답이 보입니다.
자원 | CPU | GPU | ASIC |
|---|---|---|---|
코어 수 | 적음 (수~수십) | 많음 (수천) | 매우 많음 |
코어당 빠른 메모리(SRAM) | 크다 | 작다 | 매우 작다 |
단순 산술 처리량 | 보통 | 매우 높음 | 극도로 높음 |
GPU와 ASIC은 연산 코어는 많지만, 코어 하나가 쓸 수 있는 빠른 메모리는 매우 제한적입니다. 만약 알고리즘이 코어당 수십~수백 MB의 메모리를 요구하도록 설계되면, GPU와 ASIC은 코어 대부분을 놀려야 합니다. 가지고 있는 메모리가 부족하니까요.
비유하자면 이렇습니다.
CPU 공격은 "혼자서 100문제 풀기"입니다. GPU 공격은 "1000명이 각자 100문제 풀기"입니다. Memory-Hard 함수는 각 문제마다 "두꺼운 참고서 한 권"이 필요하게 만듭니다. GPU가 1000명을 동원해도, 참고서 1000권을 살 공간이 없으면 의미가 없어집니다.
scrypt: 최초의 실용적 Memory-Hard KDF
scrypt는 다음 세 가지 파라미터를 가집니다.
N(CPU/메모리 비용) — 메모리 사용량r(블록 크기) — 메모리 접근 패턴p(병렬화 정도) — 동시 실행 스레드 수
라이트코인이 채굴 알고리즘으로 scrypt를 채택한 것도 같은 이유였습니다. 비트코인의 SHA-256은 ASIC에 점령당했지만, scrypt는 메모리 요구사항 덕분에 한동안 일반 GPU로도 채굴이 가능했지요(물론 결국 scrypt ASIC도 등장합니다).
scrypt의 한계
scrypt는 강력하지만 세 가지 파라미터의 트레이드오프가 직관적이지 않다는 단점이 있었습니다. 또한 측면 채널 공격(side-channel attack)에 대한 방어가 명시적이지 않았지요.
여기서 더 정교한 후속 알고리즘이 필요해졌습니다.
3세대: Argon2id — 현재의 표준
Argon2의 탄생: Password Hashing Competition
2013년, 학계와 업계는 차세대 비밀번호 해싱 표준을 정하기 위해 공개 경연을 엽니다. PHC(Password Hashing Competition) 이지요.
24개 알고리즘이 출품됐고, 2년간의 검증을 거쳐 2015년 Argon2 가 우승합니다. 룩셈부르크 대학교 연구팀(Alex Biryukov, Daniel Dinu, Dmitry Khovratovich)이 만든 알고리즘이지요.
Argon2의 세 가지 변종
Argon2는 용도에 따라 세 가지 변종이 있습니다.
변종 | 특징 | 사용처 |
|---|---|---|
Argon2d | 데이터 의존적 메모리 접근. GPU/ASIC 저항 최강 | 측면 채널 공격 위험이 없는 환경 (암호화폐 채굴 등) |
Argon2i | 데이터 독립적 메모리 접근. 측면 채널 공격 방어 | 측면 채널 위험이 있는 환경 (서버 비밀번호 해싱) |
Argon2id | 위 둘의 하이브리드 | 대부분의 일반적 비밀번호 해싱 |
Argon2id가 현재 권장되는 기본 선택입니다. OWASP, NIST, IETF RFC 9106 모두 Argon2id를 1순위로 권고합니다.
세 가지 파라미터로 정교한 제어
Argon2는 세 가지 비용 파라미터를 가집니다.
시간 비용 (
t) — 반복 횟수메모리 비용 (
m) — 사용할 메모리 양 (KiB)병렬성 (
p) — 동시 처리 스레드 수
OWASP 2024 권고치는 다음과 같습니다.
m = 19456 (19 MiB)
t = 2
p = 1이 세 파라미터로 하드웨어 환경에 맞춰 보안 수준을 정교하게 조절할 수 있습니다.
왜 Argon2id가 "끝판왕"인가
Argon2id가 표준이 된 이유는 명확합니다.
메모리-하드 속성 — GPU/ASIC 공격에 강함
측면 채널 방어 — Argon2i의 데이터 독립적 접근 덕분
정교한 파라미터 — 시간·메모리·병렬성 모두 조절 가능
공개 검증 — 2년간의 PHC 심사를 통과
국제 표준 — RFC 9106으로 공식 표준화
물론 Argon2id도 영원하지는 않을 겁니다. 양자 컴퓨팅, 새로운 공격 모델이 나오면 또 다른 세대가 등장하겠지요.
왜 이런 변화가 일어 났을까?
각 세대는 이전 세대의 어떤 한계를 해결하기 위해 등장했는지 정리하면 흐름이 명확해집니다.
[1세대] 단순 SHA-256
↓ 문제: 너무 빠름. GPU로 초당 수백억 회 시도 가능
[2세대] PBKDF2 (Key Stretching)
↓ 문제: CPU 연산만 사용 → GPU/ASIC에 여전히 취약
[중간] scrypt (Memory-Hard 도입)
↓ 문제: 파라미터 트레이드오프 복잡, 측면 채널 미보호
[3세대] Argon2id (메모리-하드 + 측면 채널 방어 + 정교한 파라미터)이 진화를 관통하는 원리는 두 가지입니다.
원리 1: 하드웨어 진화에 대한 대응
방어 기술의 진화는 결국 공격자의 하드웨어 진화에 대응하는 과정이었습니다.
시기 | 공격자 무기 | 방어 답 |
|---|---|---|
2000년대 초 | 일반 CPU | SHA-256 |
2000년대 중후반 | 빠른 CPU, 대용량 저장공간 (레인보우 테이블) | PBKDF2 + Salt |
2010년대 초 | GPU 보편화 | scrypt (Memory-Hard) |
2010년대 중반~ | ASIC, 클라우드 컴퓨팅 | Argon2id |
원리 2: 비대칭 비용의 극대화
모든 세대의 공통 목표는 같습니다.
정당 사용자에게는 거의 무료. 공격자에게는 비현실적으로 비싸게.
각 세대는 비대칭을 만드는 새로운 차원을 추가해 왔습니다.
SHA-256: 차원 없음 (대칭)
PBKDF2: 시간 차원 추가
scrypt: 메모리 차원 추가
Argon2id: 시간 + 메모리 + 병렬성 차원 추가
차원이 늘어날수록 공격자가 우회할 길이 줄어듭니다.
성능 및 특성 비교표
항목 | SHA-256 | PBKDF2 | scrypt | Argon2id |
|---|---|---|---|---|
표준화 연도 | 2001 (FIPS 180-4) | 2000 (RFC 2898) | 2016 (RFC 7914) | 2021 (RFC 9106) |
유형 | 해시 함수 | KDF | KDF (Memory-Hard) | KDF (Memory-Hard) |
시간 비용 | 매우 빠름 (μs) | 느림 (조절 가능) | 느림 (조절 가능) | 느림 (조절 가능) |
메모리 사용 | 거의 없음 | 거의 없음 | 수십 MB~ | 수십 MB~ |
GPU 저항 | ❌ 매우 약함 | ⚠️ 약함 | ✅ 강함 | ✅ 강함 |
ASIC 저항 | ❌ 매우 약함 | ❌ 약함 | ⚠️ 중간 | ✅ 강함 |
측면 채널 방어 | 해당 없음 | 부분적 | ❌ 미흡 | ✅ 강함 |
파라미터 수 | 없음 | 1개 (반복) | 3개 (N, r, p) | 3개 (t, m, p) |
FIPS 인증 | ✅ | ✅ | ❌ | ❌ (검토 중) |
OWASP 권고 (2024) | 비밀번호 저장에 부적합 | 600,000 iter | 권장 가능 | 1순위 권장 |
주요 사용처 | 무결성 검증, 블록체인 | 이더리움 키스토어, BIP-39 | 라이트코인, 일부 지갑 | 일반 비밀번호 해싱, 신규 시스템 |
일반적인 비밀번호 1억 개 공격 시 예상 비용 (개념적 비교)
알고리즘 | 추정 공격 시간 (GPU 1대 기준) |
|---|---|
SHA-256 단일 해시 | 약 5초 |
PBKDF2 (60만 회) | 약 1.5년 |
scrypt (16MB 메모리) | 약 50년 |
Argon2id (19MiB 메모리) | 약 100년+ |
※ 추정치이며 하드웨어와 파라미터에 따라 달라집니다. 절대값보다 자릿수 차이가 의미 있습니다.
실제 사용처 (2024 기준)
알고리즘 | 어디에 쓰나? |
|---|---|
SHA-256 | 파일 무결성 검증, 디지털 서명, 비트코인 채굴 |
PBKDF2 | 이더리움 키스토어, BIP-39 시드 생성, 정부/금융 인증 환경 |
scrypt | 라이트코인, 일부 암호화폐 지갑 |
Argon2id | 신규 시스템의 비밀번호 해싱 (OWASP 1순위) |
정리 : 그래서 뭘 써야 하나?
마지막으로 가장 실용적인 질문에 답해 봅시다. 상황별 선택지입니다.
새로운 서비스를 만들고 있다 → Argon2id 특별한 이유가 없는 한 Argon2id를 쓰세요.
정부/금융 인증이 필요한 시스템이다 → PBKDF2 FIPS 140 인증이 필요한 환경에서는 PBKDF2가 여전히 합리적입니다. (Argon2는 아직 FIPS 인증 진행 중)
임베디드/모바일 환경, 메모리가 부족하다 → PBKDF2 메모리를 많이 못 쓰는 환경에서는 Argon2id가 부담스러울 수 있어요.
이미 BIP-39, 이더리움 키스토어 등 표준을 따라야 한다 → 정해진 대로 표준이 PBKDF2를 지정했다면 따라야 합니다. 개인 결정 사항이 아니에요.
파일 무결성 검증, 디지털 서명이 필요하다 → SHA-256 이건 비밀번호 저장이 아니라 다른 용도예요. SHA-256이 표준입니다.