약 2개월 전
조회수 209수정됨

[기술블로그] bcrypt에서 Argon2id로 — 핀태그 비밀번호 해싱 전환기

[기술블로그] bcrypt에서 Argon2id로 — 핀태그 비밀번호 해싱 전환기

"Security is not a product, but a process." — Bruce Schneier

안녕하세요! 핀태그 기술 개발팀 입니다!

보안은 한 번 사서 끝나는 제품이 아니라, 계속 갈고닦아야 하는 과정이라는 말입니다.
핀태그는 기업의 금융 데이터를 다루는 서비스입니다. 우리에게 보안은 협상 가능한
옵션이 아니라 서비스의 기본값이어야 한다고 믿습니다.
오늘은 그 믿음을 실천한 사례 하나를 공유합니다. 핀태그는 최근 비밀번호 보호 체계를
업계 표준이었던 bcrypt에서, 현 시점 가장 권장되는 알고리즘인 Argon2id
전환했습니다. 왜 바꿨는지, 무엇이 더 안전해졌는지, 그리고 어떻게 사용자가 아무것도
느끼지 못하게 전환했는지 이야기해 보겠습니다.

1. 핀태그는 여러분의 비밀번호를 모릅니다

먼저 전제부터. 제대로 만든 서비스는 사용자의 비밀번호를 저장하지 않습니다.
저장하는 것은 비밀번호를 일방향 함수에 통과시킨 해시(hash)​ 값입니다.
"MyP@ssw0rd!" → 해시 함수 → "$argon2id$..." (복원 불가능한 문자열)
해시는 한 방향으로만 계산됩니다. 비밀번호에서 해시를 만들 수는 있지만, 해시에서
비밀번호를 되돌릴 수는 없습니다. 로그인할 때는 입력한 비밀번호를 같은 방식으로
해시해서 저장된 값과 비교할 뿐입니다. 그래서 만에 하나 데이터베이스가 유출되더라도
공격자가 손에 쥐는 것은 비밀번호가 아니라 해시 더미입니다.
문제는, 공격자가 그 해시 더미를 가지고 무차별 대입(brute-force)​을 시도한다는
것입니다. 흔한 비밀번호 후보를 하나씩 해시해 보면서 일치하는 값을 찾는 거죠.
그래서 비밀번호 해시 함수의 성능 지표는 일반 함수와 정반대입니다.
의도적으로 느리고, 의도적으로 비싸야 좋은 함수입니다.

2. bcrypt는 틀리지 않았습니다. 다만 시대가 변했습니다

bcrypt는 1999년에 설계된 이후 25년 넘게 업계 표준 자리를 지켜온 훌륭한
알고리즘입니다. 핀태그도 초기부터 bcrypt를 사용했고, 지금도 bcrypt 자체가
"뚫린" 것은 아닙니다.
변한 것은 공격자의 하드웨어입니다.
bcrypt가 설계되던 시절의 공격자는 CPU로 해시를 계산했습니다. 그런데 bcrypt의
연산은 약 4KB의 메모리만 사용합니다. 메모리를 거의 쓰지 않는 연산은 GPU의
수천 개 코어에 태워 대량으로 병렬화하기 좋습니다. 암호화폐 채굴 붐 이후
고성능 GPU는 흔한 장비가 됐고, 클라우드에서 시간 단위로 빌릴 수도 있습니다.
공격 비용이 25년 전과는 비교할 수 없게 낮아진 것입니다.
방어 기술이 그대로인데 공격 비용이 떨어졌다면, 방어를 올려야 합니다.
보안 기술은 도입 시점이 아니라 공격자의 현재에 맞춰야 합니다.​

3. Argon2id — 메모리를 무기로 쓰는 해시

Argon2는 2015년 Password Hashing Competition(PHC)​ 에서 전 세계 암호학자들의
검증을 거쳐 우승한 알고리즘입니다. 이후 IETF 표준(RFC 9106)이 되었고, OWASP가
비밀번호 저장 가이드에서 1순위로 권장하는 알고리즘이기도 합니다.
핵심 아이디어는 단순하고 강력합니다. 연산에 메모리를 강제로 쓰게 만드는 것
(memory-hard)입니다.
  • bcrypt: 해시 1개 계산에 메모리 약 4KB → GPU 코어 수천 개에서 동시 계산 가능
  • Argon2id: 해시 1개 계산에 수천 배의 메모리를 강제 → 같은 GPU에서 동시에
    계산할 수 있는 개수가 메모리 용량에 묶여 급감
연산 속도는 병렬화로 살 수 있지만, 코어마다 큰 메모리를 쥐여주는 것은 훨씬
비쌉니다. 공격자의 가장 강력한 무기(대량 병렬화)를 경제적으로 무력화하는
설계입니다.
이름 끝의 id도 의미가 있습니다. Argon2에는 두 가지 변형이 있는데,
GPU 공격에 강한 Argon2d와 부채널(side-channel) 공격에 강한 Argon2i입니다.
Argon2id는 두 방식을 결합한 하이브리드로, 두 공격 모두에 균형 잡힌 방어를
제공합니다. RFC와 OWASP가 권장하는 것도 이 id 변형입니다.
핀태그는 OWASP가 공개한 권장 기준 이상의 파라미터로 운영 환경에 맞게 설정해
적용했습니다.

4. 여기서 한 걸음 더 — Pepper

핀태그는 Argon2id 위에 페퍼(pepper)​ 를 한 겹 더 올렸습니다.
솔트(salt)는 해시마다 다른 무작위 값으로, 같은 비밀번호도 다른 해시가 되게
만들어 레인보우 테이블 공격을 막습니다(Argon2id에 기본 내장). 페퍼는 여기에
더해지는 서버만 아는 비밀 키입니다. 핀태그는 비밀번호를 페퍼 키로
HMAC-SHA256 처리한 뒤 Argon2id에 넣습니다.
저장 해시 = Argon2id( HMAC-SHA256(key = pepper, msg = 비밀번호) )
페퍼는 데이터베이스가 아니라 격리된 별도의 시크릿 관리 인프라
보관됩니다. 의미는 분명합니다. 데이터베이스가 통째로 유출되는 최악의 상황에도,
페퍼가 없는 공격자는 크래킹 시도 자체를 시작할 수 없습니다.​
해시 더미가 아니라
잠긴 금고를 손에 넣은 셈이 됩니다.

5. 아무도 눈치채지 못한 마이그레이션

알고리즘을 바꾸기로 했을 때 가장 어려운 질문은 기술이 아니라 운영입니다.
"기존 사용자의 bcrypt 해시는 어떻게 하지?"
해시는 되돌릴 수 없으므로 서버가 일괄 변환할 방법이 없습니다. 전 사용자에게
비밀번호 재설정을 요구하는 서비스도 있지만, 우리는 그 불편을 사용자에게
전가하지 않기로 했습니다. 대신 로그인 시점 점진 전환(rehash-on-login)​
구현했습니다.
로그인 시도
 ├─ 저장된 해시가 bcrypt 형식인가?
 │    └─ bcrypt로 검증 → 성공하면 그 자리에서
 │       Argon2id + 페퍼 해시로 다시 저장
 └─ 이미 Argon2id인가? → 바로 검증
사용자는 평소처럼 로그인할 뿐이지만, 그 순간 계정의 보호 체계는 최신으로
올라갑니다. 단 한 명도 비밀번호를 다시 설정하지 않았고, 단 1초의 점검 시간도
없었습니다.
운영 안정성도 함께 설계했습니다. Argon2id는 의도적으로 메모리를 쓰는 연산이라,
로그인이 한꺼번에 몰리면 서버 메모리를 압박할 수 있습니다. 핀태그는 비밀번호
연산의 동시 실행 수를 제한하는 큐(세마포어)를 두어, 보안 강화가 서비스 안정성을
해치지 않도록 했습니다. 보안과 가용성은 둘 중 하나를 고르는 문제가 아니라,
둘 다 설계하는 문제입니다.​

6. 마치며 — 보안은 타협의 대상이 아닙니다

이번 전환으로 사용자가 새로 누르게 된 버튼은 하나도 없습니다. 화면도 그대로,
로그인도 그대로입니다. 보안 작업이란 원래 그런 것입니다. 잘 될수록 아무 일도
없던 것처럼 보입니다.
하지만 그 보이지 않는 곳에서, 여러분의 비밀번호는 이제 국제 표준 기구와
보안 커뮤니티가 검증한 가장 강력한 방식으로 보호되고 있습니다.
핀태그는 기업의 금융 데이터를 다룹니다. 우리는 보안을 비용으로 보지 않습니다.
보안은 타협의 대상이 아니라, 금융 서비스가 신뢰를 말할 자격의 최소 조건입니다.​
공격자의 도구가 진화하는 한 우리의 방어도 계속 진화할 것이고, 그 과정은 앞으로도
이렇게 투명하게 공유하겠습니다.

핀태그 기술블로그는 핀태그가 서비스를 만들며 내린 기술적 결정과 그 이유를
공유합니다. 문의: support@fintag.kr

[기술블로그] bcrypt에서 Argon2id로 — 핀태그 비밀번호 해싱 전환기

관련 게시글

더 많은 유용한 정보를 확인해보세요