이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
공부 › 보안 › 인증
비밀번호 해시
DB가 털렸을 때 비밀번호는 얼마나 버티나
회원 두 명이 우연히 같은 비밀번호(password123)를 씁니다. 비밀번호를 DB에 저장하는 방식에 따라, DB가 유출됐을 때 공격자가 원래 비밀번호를 알아내는 데 걸리는 시간이 크게 달라집니다. 해시는 원래 값으로 되돌릴 수 없게 계산해 낸 값입니다. 평문, 해시, 솔트를 넣은 해시, 일부러 느리게 만든 해시(bcrypt)를 비교합니다.
일부만 실행으로 확인 · SHA-256은 실제 계산, bcrypt는 재지 못함
이 실습에 나오는 개념
- 해시 함수와 솔트
- 해시 함수는 입력을 고정된 길이의 값으로 바꾸는 함수로, 결과에서 원래 입력을 되돌릴 수 없다. 비밀번호는 원문 대신 해시 값을 저장해 유출되더라도 원문이 드러나지 않게 한다. 솔트는 비밀번호마다 다르게 덧붙이는 무작위 값으로, 같은 비밀번호라도 다른 해시가 되게 해 미리 계산해 둔 해시 목록을 이용한 공격을 막는다.
- bcrypt
- 비밀번호 저장용으로 설계된 해시 함수다. 솔트를 자동으로 생성해 결과에 포함하고, 연산 비용(cost)을 설정해 일부러 계산을 느리게 만든다. 계산이 느리면 공격자가 수많은 후보를 대입해 보는 데 드는 시간이 크게 늘어난다. Spring Security의 BCryptPasswordEncoder가 이를 사용한다.
- 대칭키 암호화와 비대칭키 암호화
- 대칭키 암호화는 암호화와 복호화에 같은 키를 쓰는 방식으로 빠르지만 키를 상대와 안전하게 공유해야 하는 문제가 있다(AES). 비대칭키 암호화는 공개키와 개인키의 쌍을 쓰는 방식으로, 공개키로 암호화한 것은 개인키로만 풀 수 있어 키 공유 문제가 없지만 느리다(RSA). 실제로는 비대칭키로 대칭키를 안전하게 교환한 뒤 대칭키로 데이터를 암호화한다.
이렇게 바꿔 보세요
- 처음 설정으로 — SHA-256. 같은 비밀번호는 같은 해시가 됩니다.
- 평문 저장 — 유출 즉시 전부 노출됩니다.
- 솔트를 추가하면 — 같은 비밀번호도 해시가 달라집니다. 하지만 해시 자체가 빠릅니다.
- bcrypt — 일부러 느리게 만든 해시입니다. 대입에 걸리는 시간이 달라집니다.
- bcrypt cost 14 — 공격은 더 느려지지만 로그인 검증도 느려집니다.
처음 설정으로 실행하면
- 회원 김철수와 이영희가 가입하면서 둘 다 비밀번호를 password123으로 정했습니다. 서버가 SHA-256 방식으로 DB에 저장합니다.
- 두 회원의 저장값이 완전히 같습니다. DB를 본 사람은 두 사람의 비밀번호가 같다는 것을 바로 알 수 있고, 하나를 알아내면 같은 값을 가진 모든 회원의 비밀번호를 알게 됩니다.
- 사고: 공격자가 DB의 회원 테이블을 통째로 빼냈습니다.
- 해시는 거꾸로 계산할 수 없지만, 흔한 비밀번호의 해시를 미리 계산해 둔 표가 이미 존재합니다. 공격자는 저장값을 표에서 찾기만 하면 됩니다. password123의 SHA-256은 누구에게나 같은 값이기 때문입니다.
- 흔한 비밀번호 1억 개를 회원 한 명에게 모두 대입해 보는 데 걸리는 시간: 1초 미만. password123처럼 흔한 비밀번호는 사실상 즉시 찾아냅니다.
실행 결과
- SHA-256 같은 일반 해시 함수는 파일 검증처럼 빨라야 하는 용도로 만들어졌습니다. 비밀번호 저장에는 "빠르다"는 것이 약점입니다.
- 비밀번호는 암호화가 아니라 해시로 저장합니다. 암호화는 열쇠가 있으면 원래 값으로 되돌릴 수 있지만, 해시는 되돌릴 방법이 없고 서버도 원래 비밀번호를 알 필요가 없습니다.
코드
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(rawPassword.getBytes(UTF_8));
member.setPassword(HexFormat.of().formatHex(hash));
이 모형의 전제
SHA-256 값은 실제로 계산한 값입니다. bcrypt 저장값은 형식을 보이기 위한 예시 문자열입니다. 대입 속도는 대략적인 크기를 나타냅니다. 고성능 그래픽 카드 한 장 기준으로 SHA-256은 초당 수백억 번, bcrypt cost 10은 초당 수천 번 수준이라고 보았고, 공개된 벤치마크의 자릿수에 맞춘 값입니다. 로그인 검증 시간도 서버 CPU에 따라 달라지는 어림값입니다. 스프링 시큐리티의 BCryptPasswordEncoder 기본 cost는 10입니다. 이 PC에서 openssl로 잰 SHA-256 속도는 CPU 코어 하나로 초당 약 3,200만 번이었습니다(화면의 초당 수백억 번은 그래픽 카드 기준의 공개 수치입니다). bcrypt는 이 PC에 라이브러리가 없어 실제로 재지 못했습니다.
앞 실습: 쿠키 · 다음 실습: JWT의 구조와 검증