사전 비밀번호
개념

비밀번호

gabury1고친 사람 github-actions[bot]

비밀번호는 로그인할 때 계정 주인이 맞는지 가려 줍니다. 주인만 아는 글자를 입력하게 해서 남과 구별합니다. 서버는 이 글자를 받은 모습으로 두지 않고 되돌릴 수 없는 값으로 바꿔 저장합니다.

쉽고 빠른 이해

비밀번호는 계정 주인만 아는 글자로 주인을 가려내는 수단입니다. 쇼핑몰에 가입할 때 정해 두고 로그인할 때마다 입력하는 그 값입니다.

서버는 화면 너머의 사람을 볼 수 없습니다. 주인만 아는 값을 확인하지 않으면 누구든 남의 계정에 들어갈 수 있습니다.

  1. 가입할 때 서버가 비밀번호를 되돌릴 수 없는 값으로 바꿔 저장합니다
  2. 로그인할 때 들어온 비밀번호를 같은 방법으로 바꿉니다
  3. 두 값이 같으면 주인으로 받아들입니다

대가는 아는 사람이면 누구나 주인 행세를 한다는 것입니다. 새어 나간 비밀번호는 서버가 가려낼 수 없습니다. 사람은 흔한 비밀번호를 고르고 여러 서비스에 같은 것을 쓰기도 합니다.

비밀번호는 사람이 외워서 로그인하는 곳에 씁니다. 중요한 계정에는 휴대폰 같은 수단을 하나 더 붙입니다. 프로그램끼리 부를 때는 외울 사람이 없어서 서비스가 만들어 준 긴 값을 대신 씁니다.

상세

현관 도어락은 번호를 누른 사람에게 문을 열어 줍니다. 열쇠를 챙길 필요 없이 머릿속 숫자 몇 개면 됩니다. 문은 번호만 볼 뿐 누가 눌렀는지는 보지 않습니다.

비밀번호는 인증에 쓰는 비밀 값입니다. 인증은 요청을 보낸 쪽이 누구인지 가리는 일입니다.

사용자는 가입할 때 이 값을 직접 골라 둡니다. 로그인할 때마다 같은 값을 다시 입력합니다. 서버는 그 값이 가입 때 것과 같은지를 보고 계정 주인으로 받아들입니다.

서버는 네트워크 너머에 있는 사람을 볼 수 없습니다. 들어오는 것은 글자뿐입니다. 그 글자는 누구든 똑같이 만들어 보낼 수 있습니다. 그래서 주인만 알 법한 값을 하나 정해 두고 매번 그 값을 가져오게 합니다.

비밀번호에는 준비물이 없습니다. 카드도 기계도 필요 없이 어느 기기에서든 입력하면 됩니다. 인증 수단 가운데 비밀번호가 가장 널리 쓰이는 까닭이 이것입니다.

인증 요소 세 갈래

주인을 가리는 근거를 인증 요소라고 부릅니다. 흔히 세 갈래로 나눕니다. 비밀번호는 그중 「아는 것」에 속합니다.

갈래 무엇으로 가리나 예
아는 것 머릿속에 든 값 비밀번호 · 휴대폰 [[락
가진 것 손에 쥔 물건 휴대폰 · 보안 키
그 사람 자체 몸의 특징 지문 · 얼굴

표에서 아는 것만 성질이 다릅니다. 가진 것은 잃어버리면 주인이 알아챕니다. 아는 것은 누가 엿보거나 옮겨 적어도 주인 머릿속에 그대로 남습니다. 비밀번호의 약점은 대부분 여기서 나옵니다.

가입과 로그인에서 비밀번호가 지나가는 길

이 소절은 서버가 비밀번호를 받아 저장하고 다시 맞춰 보는 순서를 봅니다. 요점은 서버가 비밀번호 원문을 어디에도 남기지 않는다는 것입니다.

받은 글자를 손대지 않은 채 저장한 것을 평문 저장이라고 부릅니다. 평문으로 두면 저장소가 새는 순간 모든 계정의 비밀번호가 함께 샙니다. 저장소를 들여다볼 수 있는 내부 사람에게도 전부 보입니다.

그래서 해시 함수를 거친 값만 저장합니다. 해시 함수는 입력을 정해진 길이의 다른 값으로 바꿉니다. 결과만 보고 입력을 거꾸로 알아낼 수는 없습니다. 같은 입력은 언제나 같은 결과가 되므로 로그인 때 한 번 더 계산해 견주면 됩니다.

같은 비밀번호가 늘 같은 결과가 되면 틈이 생깁니다. 흔한 비밀번호의 결과를 미리 계산해 표로 만들어 두면 훔친 목록 전체를 한꺼번에 맞춰 볼 수 있습니다. 이런 표 가운데 널리 알려진 것이 레인보우 테이블입니다.

이 틈은 사용자마다 다른 무작위 값을 비밀번호에 섞어서 막습니다. 이 값을 솔트라고 부릅니다. 솔트는 비밀이 아니라서 해시 결과 옆에 함께 저장합니다.

공격하는 쪽이 솔트를 알아도 미리 만든 표는 쓸 수 없습니다. 그 표는 솔트 없이 계산한 것이라 솔트가 섞인 결과는 표에 없습니다. 솔트가 사용자마다 다르니 표도 사용자마다 새로 만들어야 합니다. 그러면 미리 만들어 두는 이득이 사라집니다.

비밀번호용 해시는 계산 한 번이 일부러 오래 걸리게 만듭니다. 로그인 한 번에는 티가 안 날 만큼만 늦춥니다. 대신 훔친 해시를 자기 컴퓨터에서 횟수 제한 없이 맞혀 보는 쪽은 추측 하나하나가 그만큼 오래 걸립니다.

이렇게 저장하는 방법 전체가 비밀번호 해싱입니다. bcrypt · Argon2id 같은 함수가 이 일을 합니다.

sequenceDiagram
    participant 사용자
    participant 서버
    participant 저장소
    Note over 사용자,저장소: 가입
    사용자->>서버: 아이디와 비밀번호
    서버->>서버: 솔트를 뽑아 섞고 해시한다
    서버->>저장소: 솔트와 해시 결과만 적는다
    Note over 사용자,저장소: 로그인
    사용자->>서버: 아이디와 비밀번호
    서버->>저장소: 이 아이디의 솔트와 해시 결과를 달라
    저장소-->>서버: 솔트 · 해시 결과
    서버->>서버: 같은 솔트로 다시 해시해 견준다
    서버-->>사용자: 같으면 로그인 성공

그림에서 볼 것은 로그인 쪽입니다. 서버는 저장해 둔 값에서 비밀번호를 되살려 비교하지 않습니다. 들어온 비밀번호로 가입 때와 같은 계산을 한 번 더 합니다. 그 결과가 저장해 둔 값과 같은지 봅니다.

코드로 옮기면 저장하는 함수와 확인하는 함수 둘로 나뉩니다. 아래의 hash_password 와 verify 는 설명을 위해 지은 이름입니다.

Python
h = hash_password("hunter2")  # 솔트+결과

verify("hunter2", h)  # True
verify("Hunter2", h)  # False

h 는 솔트와 해시 결과를 한 문자열에 담고 있습니다. 대문자 하나만 달라도 해시 결과가 완전히 달라져 틀린 것으로 나옵니다. 서버가 가진 것은 h 뿐입니다. h 에서 hunter2 를 꺼낼 방법은 없습니다.

비밀번호를 노리는 공격

비밀번호는 대개 맞혀지거나 새어 나갑니다. 이 소절은 공격이 들어오는 길을 넷으로 나눠 봅니다.

공격 어떻게 하나 서버 쪽에서 막는 수단
무차별 대입 공격 가능한 조합을 하나씩 전부 넣어 본다 로그인 시도 횟수 제한
사전 공격 사람들이 흔히 쓰는 비밀번호부터 넣어 본다 흔한 비밀번호로는 가입을 막는다
크리덴셜 스터핑 다른 서비스에서 샌 아이디와 비밀번호 쌍을 넣어 본다 인증 요소를 하나 더 요구한다
피싱 가짜 로그인 화면을 띄워 사용자가 직접 입력하게 만든다 입력할 값이 없는 로그인(패스키, 아래 소절)

앞의 셋은 서버에 로그인을 직접 되풀이해 시도합니다. 그래서 같은 계정이나 같은 곳에서 짧은 동안 들어오는 시도 수를 세어 막습니다. 이렇게 횟수에 상한을 두는 장치를 속도 제한이라고 부릅니다.

크리덴셜 스터핑이 통하는 까닭은 한 사람이 여러 서비스에 같은 비밀번호를 쓰기 때문입니다. 어느 한 곳이 새면 다른 서비스의 계정까지 함께 열립니다. 우리 서비스가 안 샜어도 당하는 공격입니다.

저장소가 통째로 새면 이야기가 달라집니다. 공격하는 쪽은 훔친 해시를 자기 컴퓨터로 가져가 횟수 제한 없이 맞혀 봅니다. 속도 제한은 여기서 소용이 없습니다. 솔트와 오래 걸리는 해시가 막으려는 것이 이 경우입니다.

맞히기 어려운 비밀번호

이 소절은 비밀번호가 얼마나 맞히기 어려운지를 무엇이 정하는지 봅니다. 가능한 조합의 수를 세어 보면 길이와 글자 종류 가운데 어느 쪽이 더 크게 작용하는지 드러납니다.

무작위로 고른 비밀번호라면 조합의 수는 글자 종류의 수를 길이만큼 거듭 곱한 값입니다. 소문자만 쓰면 한 글자에 26가지이고, 대소문자와 숫자를 섞으면 62가지입니다.

비밀번호 조합의 수
소문자 8자 약 2천억
대소문자와 숫자 8자 약 218조
소문자 12자 약 9경

표에서 소문자 12자가 대소문자와 숫자를 섞은 8자를 훨씬 넘습니다. 한 자리 늘 때마다 전체에 글자 종류의 수가 한 번 더 곱해지기 때문입니다. 낱말 여러 개를 이어 길이를 늘린 비밀번호를 패스프레이즈라고 부릅니다. 기호를 섞는 대신 길이로 조합을 늘리는 방법입니다.

이 계산은 무작위로 골랐을 때만 맞습니다. 사람이 고른 비밀번호는 password1 이나 생일, 키보드에서 나란한 글자처럼 흔한 쪽으로 몰립니다. 사전 공격은 이 쏠림을 노려 흔한 것부터 넣어 봅니다. 그래서 조합의 수가 커 보여도 흔한 비밀번호는 금방 맞혀집니다.

서비스마다 다른 무작위 비밀번호를 사람이 다 외우기는 어렵습니다. 비밀번호 관리자는 서비스마다 긴 무작위 비밀번호를 만들고 대신 기억해 주는 프로그램입니다. 사용자는 관리자를 여는 비밀번호 하나만 외웁니다.

서버가 비밀번호를 다룰 때 지키는 것

백엔드가 비밀번호를 받는 순간부터 지킬 것이 몇 가지 있습니다. 각각이 무엇을 막는지 함께 봅니다.

지키는 것 막는 것
오가는 내용을 TLS(Transport Layer Security, 전송 계층 보안)로 감싼다 중간에서 엿보기
원문 대신 솔트를 섞은 해시만 저장한다 저장소가 샐 때 원문까지 새는 것
로그와 오류 메시지에 비밀번호를 남기지 않는다 로그를 통한 유출
로그인 시도 횟수를 제한한다 로그인을 되풀이하는 대입
없는 아이디와 틀린 비밀번호에 같은 문구를 돌려준다 가입된 아이디 알아내기

TLS 는 주고받는 내용을 중간에 선 누구도 못 읽게 만드는 약속입니다. 이것 없이 보내면 해시를 아무리 잘해도 전송 도중에 원문이 읽힙니다.

로그 쪽 유출은 대개 요청 본문을 디버깅용으로 찍는 코드에서 생깁니다. 로그인 요청을 찍으면 비밀번호 원문이 로그 파일에 남습니다. 로그는 저장소보다 보는 사람이 많고 오래 남습니다.

마지막 줄은 계정 열거를 막습니다. 계정 열거는 오류 문구의 차이로 어느 아이디가 가입돼 있는지 알아내는 공격입니다. 가입된 아이디를 먼저 추려 내면 공격하는 쪽은 그 아이디들에만 대입을 몰아 붓습니다.

비밀번호를 잊은 사용자에게 서버는 원래 비밀번호를 알려 주지 못합니다. 서버도 원문을 모르기 때문입니다. 대신 메일 같은 다른 통로로 새 비밀번호를 정하는 일회용 링크를 보냅니다. 이 흐름을 비밀번호 재설정이라고 부릅니다.

비밀번호를 보태거나 대신하는 수단

비밀번호는 아는 것 하나에 기댑니다. 새어 나가면 서버는 주인과 남을 가릴 수 없습니다. 이 소절은 그 빈틈을 메우는 수단과 비밀번호가 애초에 맞지 않는 경우를 봅니다.

중요한 계정에는 가진 것이나 그 사람 자체를 하나 더 요구합니다. 이것을 다중 인증이라고 부릅니다. 휴대폰 앱에 뜨는 일회용 비밀번호를 함께 입력하게 하는 것이 흔한 예입니다. 비밀번호가 새도 휴대폰이 없으면 로그인이 안 됩니다.

비밀번호를 아예 없애는 방식도 있습니다. 패스키는 사용자 기기 안에 든 키로 로그인합니다. 여기서 키는 기기가 스스로 만들어 간직하는 긴 비밀 값입니다.

로그인할 때 기기는 이 키로 서버가 낸 문제에 답을 만들어 돌려줍니다. 키 자체는 서버로 보내지 않습니다. 사용자가 입력하는 값이 없어서 가짜 화면에 옮겨 적힐 것도 없습니다.

비밀번호는 사람이 외워서 입력하는 흐름에 맞춘 수단입니다. 프로그램끼리 부를 때는 외울 사람이 없습니다. 프로그램이 다른 프로그램의 기능을 불러 쓰는 통로를 API(Application Programming Interface, 응용 프로그램 인터페이스)라고 합니다.

API 를 부를 때는 비밀번호 대신 서비스가 프로그램마다 만들어 준 긴 값을 요청에 실어 보냅니다. 이 값을 API 키라고 부릅니다.

관련 항목

비밀번호가 속하는 상위 분류

인증 · 인증 요소 · 자격증명 · 인증과 인가 · 접근 제어

비밀번호를 저장할 때 거치는 계산

비밀번호 해싱 · 해시 함수 · 솔트 · 페퍼 · 키 스트레칭 · 키 유도 함수 · bcrypt · scrypt · PBKDF2 · Argon2id · 평문

비밀번호를 노리는 공격

무차별 대입 공격 · 사전 공격 · 크리덴셜 스터핑 · 레인보우 테이블 · 피싱 · 계정 열거 · 계정 탈취 · 중간자 공격 · 타이밍 공격 · 키로거

서버가 비밀번호를 받을 때 거는 방어

TLS · 속도 제한 · 계정 잠금 · CAPTCHA · 비밀번호 정책 · 비밀번호 재설정

비밀번호를 보태거나 대신하는 인증 수단

다중 인증 · 일회용 비밀번호 · 패스키 · WebAuthn · 보안 키 · 생체 인증 · PIN · 싱글 사인온 · API 키 · 액세스 토큰

비밀번호의 강도를 재고 높이는 수단

엔트로피 · 패스프레이즈 · 비밀번호 관리자 · 난수 생성기

비밀번호 확인 뒤에 이어지는 단계

로그인 · 세션 · 세션 쿠키 · 인가

다른 이름: password · 패스워드