왜 비밀번호를 그냥 저장하면 안 될까
많은 서비스에서 데이터 유출 사고가 발생합니다. 만약 비밀번호를 평문(그대로) 저장했다면, 해커는 별다른 노력 없이 모든 사용자의 비밀번호를 손에 넣게 됩니다. 그래서 비밀번호는 반드시 ‘해시(hash)’라는 방식으로 변환해서 저장해야 합니다. 해시는 원래 값을 알 수 없는 고정 길이의 문자열로 바꾸는 단방향 함수입니다. 예를 들어 ‘1234’라는 비밀번호를 해시하면 ‘a3f5c9…’처럼 전혀 다른 값이 나오고, 이 값을 다시 원래 비밀번호로 되돌릴 수는 없습니다.
하지만 단순히 SHA-256 같은 일반 해시 함수를 쓰면 안 됩니다. 이런 함수는 계산 속도가 매우 빨라서, 공격자가 초당 수십억 개의 비밀번호 후보를 대입해보는 ‘무차별 대입 공격(brute force)’에 취약하기 때문입니다. 이런 문제를 해결하기 위해 등장한 것이 bcrypt와 Argon2 같은 ‘비밀번호 전용 해시 함수’입니다.
bcrypt와 Argon2의 동작 원리
bcrypt는 1999년에 등장한 알고리즘으로, 해시 연산을 의도적으로 느리게 만드는 것이 핵심입니다. 이를 위해 ‘cost factor’라는 값을 조절해서 연산 반복 횟수를 늘릴 수 있습니다. 예를 들어 cost가 12라면 2의 12제곱 번만큼 내부 연산을 반복합니다. 컴퓨터 성능이 좋아질수록 cost 값을 높여서 여전히 안전한 속도를 유지할 수 있습니다. 또한 bcrypt는 매번 다른 ‘salt(솔트)’ 값을 자동으로 섞어서, 같은 비밀번호라도 매번 다른 해시 결과가 나오게 만듭니다. 이 덕분에 미리 계산된 해시 테이블(레인보우 테이블)을 이용한 공격도 무력화됩니다.
Argon2는 2015년 password hashing competition에서 우승한 알고리즘으로, bcrypt보다 더 발전된 방식입니다. 가장 큰 차이는 CPU 연산 시간뿐 아니라 ‘메모리 사용량’까지 의도적으로 늘린다는 점입니다. 요즘 공격자들은 GPU나 전용 하드웨어(ASIC)로 병렬 연산을 돌려서 해시를 빠르게 대입하는데, Argon2는 많은 메모리를 요구해서 이런 병렬 공격을 훨씬 어렵게 만듭니다. Argon2는 Argon2d, Argon2i, Argon2id 세 가지 변형이 있고, 실무에서는 보안성과 부채널 공격 저항성을 균형 있게 갖춘 Argon2id가 권장됩니다.
실무에서 어떻게 적용할까
실제 개발에서는 라이브러리를 사용해 간단하게 적용할 수 있습니다. 예를 들어 Node.js에서는 ‘bcrypt’ 패키지를 이용해 bcrypt.hash(password, 12)처럼 호출하면 salt 생성과 해시 계산을 모두 알아서 처리해줍니다. Argon2도 마찬가지로 ‘argon2’ 패키지를 통해 argon2.hash(password) 형태로 쉽게 사용할 수 있습니다.
정리하면, bcrypt는 오랜 기간 검증되어 안정성이 높고 대부분의 언어에서 지원되는 장점이 있고, Argon2는 최신 공격 기법에 더 강하고 메모리 기반 방어가 가능하다는 장점이 있습니다. 신규 프로젝트라면 Argon2id를, 기존 시스템과의 호환성이 중요하다면 bcrypt를 선택하는 것이 일반적인 실무 가이드입니다. 어떤 알고리즘을 쓰든 핵심은 ‘평문 저장 금지’와 ‘전용 해시 함수 사용’이라는 점을 꼭 기억해야 합니다.