비밀번호를 SHA-256으로 저장하면 안 되는 이유
SHA-256은 빠르기 때문에 비밀번호에 맞지 않습니다. 해시의 용도별 구분과 bcrypt·PBKDF2가 하는 일, 솔트의 역할을 정리했습니다.
비밀번호를 평문으로 저장하면 안 된다는 것은 널리 알려져 있습니다. 그다음에 흔히 나오는 선택이 SHA-256입니다. 널리 쓰이고, 충돌도 알려진 것이 없고, 어느 언어에나 있습니다. 그런데 비밀번호 저장에는 맞지 않습니다.
해시는 용도가 나뉜다
같은 "해시"라는 이름으로 묶이지만 요구 조건이 다릅니다.
- 무결성 확인 — 파일이 전송 중에 손상되지 않았는지 본다. 빠를수록 좋다
- 식별과 중복 판별 — 같은 내용인지 비교한다. 빠를수록 좋다
- 비밀번호 저장 — 유출됐을 때 원래 값을 못 찾게 한다. 느릴수록 좋다
앞의 둘에는 SHA-256이 적합합니다. 문제는 세 번째입니다.
빠른 것이 문제다
SHA-256은 GPU에서 초당 수십억 번 계산됩니다. 데이터베이스가 통째로 유출된 상황을 생각하면 이 속도는 공격자 쪽 성능입니다.
사람이 쓰는 비밀번호는 경우의 수가 적습니다. 유출된 비밀번호 목록과 사전, 거기에 흔한 변형 규칙(끝에 숫자 붙이기, a를 @로 바꾸기)을 더하면 후보 대부분이 몇 시간 안에 소진됩니다. 임의의 32자 문자열이라면 안전하지만, 실제 사용자가 그런 비밀번호를 쓰는 경우는 드뭅니다.
여기에 두 가지가 더해집니다.
같은 비밀번호는 같은 해시가 됩니다. 유출된 테이블에서 해시가 같은 행을 묶으면 어느 계정들이 같은 비밀번호를 쓰는지 드러납니다. 하나만 풀면 전부 풀립니다.
미리 계산해 둘 수 있습니다. 흔한 비밀번호의 해시를 표로 만들어 두고 조회만 하면 됩니다.
솔트
솔트는 비밀번호마다 다르게 붙이는 무작위 값입니다. 비밀번호와 함께 해시하고, 해시 결과와 나란히 저장합니다.
솔트가 공개되어도 상관없습니다. 목적은 값을 숨기는 것이 아니라 미리 계산해 둔 표를 무용지물로 만드는 것입니다. 사용자마다 솔트가 다르면 같은 비밀번호도 다른 해시가 되고, 공격자는 계정 하나하나를 따로 공격해야 합니다.
솔트만으로는 부족합니다. 계정 하나를 따로 공격하는 비용 자체가 낮으면 그대로 뚫립니다. 그래서 속도를 낮춰야 합니다.
느리게 만드는 알고리즘
비밀번호용 해시는 계산 비용을 조절할 수 있게 설계되었습니다.
bcrypt — 코스트 값으로 반복량을 정합니다. 값을 1 올리면 계산량이 두 배가 됩니다. 솔트를 결과 문자열 안에 포함하므로 따로 관리하지 않아도 됩니다. 입력이 72바이트에서 잘리므로 긴 비밀번호나 다국어 문자를 쓸 때 주의가 필요합니다.
PBKDF2 — 반복 횟수를 직접 지정합니다. 표준으로 인정받은 곳이 많아 규정상 선택지가 제한될 때 쓰입니다.
scrypt, Argon2 — 시간뿐 아니라 메모리도 요구합니다. GPU나 전용 하드웨어는 코어는 많아도 코어당 메모리가 적어서, 메모리를 요구하면 병렬화 이득이 줄어듭니다. 새로 만드는 시스템이라면 Argon2id가 현재 권장되는 선택입니다.
어느 쪽이든 로그인 한 번에 0.1~0.5초쯤 걸리도록 계수를 맞추는 것이 일반적인 기준입니다. 사용자는 체감하기 어렵고, 대량 시도에는 그대로 비용이 됩니다. 서버를 옮기면 같은 계수라도 걸리는 시간이 달라지므로 한 번 측정해 보는 편이 좋습니다.
옮겨야 한다면
이미 SHA-256으로 저장된 데이터가 있다면, 평문을 모르므로 한 번에 다시 계산할 수 없습니다. 두 가지 방법이 있습니다.
- 기존 해시를 다시 bcrypt로 감싸 저장하고, 어느 방식인지 표시해 둡니다
- 사용자가 다음에 로그인할 때 평문이 잠깐 서버에 있으므로, 그때 새 방식으로 다시 저장합니다
두 방법을 함께 쓰는 경우가 많습니다. 감싸는 쪽으로 즉시 위험을 낮추고, 로그인할 때마다 정상 형식으로 바꿔 나갑니다.
정리
| 용도 | 알고리즘 |
|---|---|
| 파일 무결성, 체크섬 | SHA-256, SHA-512, BLAKE3 |
| 메시지 인증 | HMAC |
| 비밀번호 저장 | Argon2id, bcrypt, scrypt, PBKDF2 |
MD5와 SHA-1은 충돌 공격이 실용화되어 서명이나 무결성 용도로는 쓰지 않습니다. 다만 비밀번호에서 이 둘이 부적합한 이유는 충돌보다 속도 쪽이 큽니다.
tools.onuel.dev에서 bcrypt와 PBKDF2로 계수를 바꿔 가며 결과를 확인할 수 있고, SHA-256이나 HMAC도 같은 자리에 있습니다. 브라우저 안에서 계산되므로 입력한 값은 서버로 가지 않습니다.
- #해시
- #비밀번호
- #보안