2단계 인증
고친 사람 github-actions[bot]
2단계 인증은 비밀번호가 맞아도 한 번 더 확인해서 본인인지 가립니다. 비밀번호만 손에 넣은 사람은 두 번째 확인에서 막힙니다. 비밀번호를 넣은 뒤 휴대폰으로 받은 숫자를 한 번 더 넣는 것이 흔한 모습입니다.
쉽고 빠른 이해
로그인 확인을 두 번에 나눠 하는 방법입니다. 비밀번호가 맞으면 서버가 휴대폰으로 숫자를 보냅니다. 그 숫자까지 맞아야 들어갑니다.
비밀번호는 한 번 새면 누가 넣든 서버가 구별하지 못합니다. 두 번째 확인이 있으면 비밀번호를 훔친 사람도 거기서 멈춥니다.
어떻게 도나:
- 서버가 비밀번호를 확인합니다
- 맞으면 아직 들여보내지 않고 두 번째 확인을 요구합니다
- 두 번째 확인까지 맞아야 로그인이 끝납니다
대가도 있습니다. 로그인이 한 번 더 길어집니다. 휴대폰을 잃으면 계정을 되찾는 절차가 따로 필요합니다. 두 번째 확인이 비밀번호와 같은 종류의 비밀이면 막는 힘이 거의 늘지 않습니다.
그래서 먼저 거는 곳은 뚫리면 피해가 큰 계정입니다. 관리자 계정이나 돈이 움직이는 계정이 그렇습니다. 사람이 없는 자동 프로그램은 두 번째 확인을 넘지 못합니다. 이런 프로그램에는 사람의 로그인 대신 프로그램 전용 키를 줍니다.
상세
아파트에 들어갈 때를 떠올려 봅니다. 공동현관에서 비밀번호를 누릅니다. 집 앞에서는 한 번 더 열쇠로 문을 엽니다. 공동현관 비밀번호를 엿본 사람도 집 열쇠가 없으면 거기서 멈춥니다.
인증은 상대가 자기가 말하는 그 사람이 맞는지 확인하는 일입니다. 2단계 인증은 이 확인을 두 단계로 나눠 차례로 하는 로그인 방식입니다. 영어로는 줄여서 2SV(Two-Step Verification)라고 씁니다.
예를 들어 사내 관리자 화면에 들어갈 때 아이디와 비밀번호를 넣습니다. 서버는 비밀번호가 맞아도 다음 화면을 띄워 휴대폰으로 보낸 여섯 자리 숫자를 달라고 합니다. 그 숫자까지 맞으면 그제야 관리자 화면이 열립니다.
비밀번호 하나로 모자란 까닭
비밀번호는 글자 몇 개라 옮겨 적기 쉽습니다. 한 번 새어 나가면 누가 입력하든 서버는 진짜 주인과 구별하지 못합니다.
새는 길 하나는 사용자가 직접 넘겨주는 것입니다. 진짜처럼 꾸민 가짜 로그인 화면에 사용자가 비밀번호를 적어 넣습니다. 이런 속임수를 피싱이라고 합니다.
다른 길은 여러 사이트에 같은 비밀번호를 쓰는 습관입니다. 공격자는 한 사이트에서 털린 아이디와 비밀번호 목록을 다른 사이트에 차례로 넣어 봅니다. 이 공격을 크리덴셜 스터핑이라고 부릅니다.
2단계 인증은 비밀번호가 새는 것 자체를 막지 않습니다. 대신 새어도 그것만으로는 들어오지 못하게 합니다. 공격자는 두 번째 확인에 쓰는 것까지 따로 손에 넣어야 합니다.
두 번째 단계에서 받는 것
두 번째 단계에서는 대개 사용자가 가진 물건을 확인합니다. 흔히 쓰는 것은 휴대폰입니다. 두 번째 단계에서 쓰는 방법은 여럿입니다. 하나씩 푼 뒤 표로 모읍니다.
가장 흔한 것은 일회용 비밀번호입니다. 한 번 쓰고 버리는 짧은 숫자입니다. 엿본 사람이 나중에 다시 넣어도 통하지 않습니다. 이 숫자를 코드라고도 부릅니다.
이 숫자를 문자로 보낼 때는 SMS(Short Message Service)라는 휴대폰 문자 서비스를 씁니다. 사용자는 따로 앱을 깔지 않아도 됩니다. 서버는 등록된 전화번호를 가진 사람이 지금 로그인하는 사람이라고 봅니다.
같은 숫자를 문자 대신 메일로 보내기도 합니다. 이것을 이메일 코드라고 부릅니다.
인증 앱은 휴대폰에 깔아 두는 숫자 생성 앱입니다. 등록할 때 서버와 앱이 비밀 값 하나를 나눠 가집니다.
로그인할 때는 서버와 앱이 각자 숫자를 계산합니다. 재료는 나눠 가진 비밀 값과 지금 시각입니다. 재료가 같으니 서로 연락하지 않아도 같은 숫자가 나옵니다. 이 계산 방식을 TOTP(Time-based One-Time Password, 시간 기반 일회용 비밀번호)라고 부릅니다.
푸시 알림 승인은 숫자를 옮겨 적는 수고를 덜어 줍니다. 서버가 등록된 휴대폰에 알림을 보냅니다. 사용자가 알림에서 승인을 누르면 그 결과가 서버로 돌아갑니다.
보안 키는 로그인 확인용으로 만든 작은 장치입니다. 서버는 로그인 때마다 다른 값을 보냅니다. 장치는 자기 안에만 든 비밀로 그 값에 서명해서 돌려줍니다. 그 비밀은 장치 밖으로 나오지 않아서 사람이 옮겨 적을 수 없습니다.
같은 일을 별도 장치 없이 휴대폰이나 노트북이 맡는 방식을 패스키라고 합니다. 사용자는 지문이나 화면 잠금을 풀어 승인합니다.
| 방식 | 사용자가 하는 일 | 사용자가 가졌다고 서버가 믿는 것 |
|---|---|---|
| 문자 메시지 코드 | 문자로 받은 숫자를 옮겨 적습니다 | 그 전화번호 |
| 이메일 코드 | 메일로 받은 숫자를 옮겨 적습니다 | 그 메일 계정 |
| 인증 앱 코드 | 앱이 보여 주는 숫자를 옮겨 적습니다 | 비밀 값이 든 휴대폰 |
| 푸시 승인 | 알림에서 승인을 누릅니다 | 등록된 휴대폰 |
| 보안 키 · 패스키 | 장치를 대거나 지문을 댑니다 | 키가 든 장치 |
표의 오른쪽 칸이 이 방식들의 약한 곳을 가리킵니다. 전화번호가 남에게 넘어가면 문자 코드도 넘어갑니다. 메일 계정이 비밀번호 하나로만 지켜지면 이메일 코드는 비밀번호를 한 번 더 묻는 것과 크게 다르지 않습니다.
로그인 한 번이 지나가는 순서
문자 메시지 코드를 예로 로그인이 어떻게 흘러가는지 봅니다. 핵심은 서버가 비밀번호 확인과 로그인 완료 사이에 기다리는 상태를 하나 둔다는 것입니다.
sequenceDiagram
participant 사용자
participant 서버
participant 휴대폰
사용자->>서버: 아이디와 비밀번호
Note over 서버: 틀리면 여기서 끝난다
Note over 서버: 맞으면 두 번째 단계를 기다리는 상태로 둔다
서버->>휴대폰: 일회용 숫자를 문자로 보낸다
휴대폰-->>사용자: 숫자를 보여 준다
사용자->>서버: 그 숫자
Note over 서버: 보낸 숫자와 맞춰 본다
서버-->>사용자: 로그인 완료
서버는 자기가 보낸 숫자를 잠깐 기억해 둡니다. 사용자가 숫자를 넣으면 그것과 맞춰 봅니다. 숫자까지 맞아야 기다리던 상태가 로그인 완료로 넘어갑니다.
단계가 둘인 것과 요소가 둘인 것
2단계 인증을 제대로 쓰려면 단계와 요소를 갈라 봐야 합니다. 단계는 확인을 몇 번 하는지입니다. 요소는 확인에 쓰는 증거가 어떤 종류인지입니다.
증거의 종류를 인증 요소라고 부릅니다. 요소는 흔히 셋으로 나눕니다.
| 요소 | 무엇인가 | 예 |
|---|---|---|
| 아는 것 | 머릿속에 든 비밀 | 비밀번호 · 비밀 질문의 답 |
| 가진 것 | 손에 쥔 물건 | 휴대폰 · 보안 키 |
| 그 자신인 것 | 몸의 특징 | 지문 · 얼굴 |
셋을 가르는 까닭은 훔치는 방법이 서로 다르기 때문입니다. 아는 것은 가짜 로그인 화면 하나로 멀리서도 빼앗깁니다. 가진 것은 그 물건을 손에 넣어야 합니다. 그 자신인 것은 사람 몸에 붙어 있습니다.
이제 두 로그인을 견줘 봅니다. 하나는 비밀번호를 넣고 다음 화면에서 「처음 다닌 학교」 같은 비밀 질문에 답합니다. 다른 하나는 비밀번호를 넣고 휴대폰으로 받은 숫자를 넣습니다. 둘 다 2단계 인증입니다.
차이는 두 번째 단계의 요소에 있습니다. 비밀 질문의 답은 비밀번호와 같은 아는 것이라 가짜 로그인 화면 하나가 둘을 한꺼번에 받아 갑니다. 휴대폰 숫자는 가진 것이라 공격자가 휴대폰까지 손에 넣어야 합니다.
서로 다른 요소를 둘 이상 쓰는 인증을 다중 인증이라고 부릅니다. 휴대폰 숫자를 쓰는 쪽은 2단계 인증이면서 다중 인증입니다. 비밀 질문을 쓰는 쪽은 2단계 인증이지만 다중 인증은 아닙니다.
서비스 화면에서는 2단계 인증이라는 말을 다중 인증과 같은 뜻으로 쓰는 일이 많습니다. 두 번째 단계에 휴대폰을 쓰는 서비스가 대부분이라 둘이 겹치기 때문입니다. 2단계 인증을 붙일 때는 이름보다 두 번째 단계가 어떤 요소인지를 먼저 봅니다.
서버가 지켜야 할 상태
2단계 인증을 붙이는 백엔드 개발자가 가장 먼저 챙길 것은 두 번째 단계를 기다리는 상태입니다. 이 상태는 로그아웃도 아니고 로그인 완료도 아닙니다. 서버는 이 상태를 세션에 적어 둡니다. 세션은 서버가 한 사용자의 로그인 상태를 요청 사이에 이어 기억하는 단위입니다.
아래 그림은 한 세션이 거치는 상태를 보입니다.
stateDiagram-v2
state "로그아웃" as out
state "두 번째 단계 대기" as half
state "로그인 완료" as done
[*] --> out
out --> half : 비밀번호가 맞음
half --> done : 두 번째 단계가 맞음
half --> out : 시간 초과 또는 실패 반복
서버는 두 번째 단계를 기다리는 세션이 보낸 요청 가운데 숫자 확인 요청만 받아 줍니다. 다른 화면이나 기능을 부르는 요청은 막아야 합니다. 이 검사를 빠뜨리면 비밀번호만으로 다른 요청이 통과합니다. 두 번째 단계를 붙여 놓고도 없는 것과 같아집니다.
기다리는 상태는 오래 두지 않습니다. 시간이 지나거나 틀린 입력이 쌓이면 로그아웃으로 되돌립니다. 그 밖에 자주 빠지는 검사를 모으면 이렇습니다.
| 검사 | 빠뜨리면 |
|---|---|
| 숫자 입력 횟수 제한 | 여섯 자리 숫자는 백만 가지뿐이라 계속 넣어 보면 맞힙니다 |
| 숫자의 유효 시간 | 오래전에 보낸 숫자가 계속 통합니다 |
| 한 번 쓴 숫자는 다시 안 받기 | 엿본 숫자를 한 번 더 쓸 수 있습니다 |
| 문자 보내기 횟수 제한 | 공격자가 남의 번호로 문자를 끝없이 보내게 만듭니다 |
입력 횟수 제한이 막는 것은 무차별 대입 공격입니다. 맞을 때까지 모든 경우를 차례로 넣어 보는 공격입니다.
한 번 쓴 숫자를 다시 안 받는 검사는 재전송 공격을 막습니다. 가로챈 값을 그대로 다시 보내는 공격입니다.
숫자 입력 횟수와 문자 보내기 횟수는 둘 다 속도 제한으로 묶습니다. 정해진 시간 안에 받는 요청 수에 상한을 두는 방법입니다.
기기 기억과 복구
로그인할 때마다 휴대폰을 꺼내게 하면 사용자가 지칩니다. 그래서 두 번째 단계를 통과한 기기를 한동안 기억해 두기도 합니다. 서버는 그 기기의 브라우저에 쿠키를 하나 남깁니다. 다음 로그인 때 그 쿠키가 있으면 두 번째 단계를 건너뜁니다.
휴대폰을 잃으면 두 번째 단계를 넘을 수 없습니다. 그래서 등록할 때 한 번씩만 쓸 수 있는 복구 코드 여러 개를 미리 줍니다. 사용자는 이를 휴대폰과 떨어진 곳에 보관합니다.
복구 길이 약하면 2단계 인증 전체가 그만큼 약해집니다. 메일로 온 링크 하나로 두 번째 단계를 끌 수 있다면 공격자는 로그인 대신 그 길로 들어옵니다. 복구에도 로그인과 같은 무게의 확인을 겁니다.
대가와 쓰는 때
로그인에 단계가 하나 늘어납니다. 문자 코드를 쓰면 문자를 보낼 때마다 돈이 듭니다. 운영하는 쪽에는 휴대폰을 잃은 사용자의 복구 문의를 처리하는 일이 생깁니다.
먼저 거는 곳은 뚫렸을 때 피해가 큰 계정입니다. 관리자 계정, 돈이 움직이는 계정, 서버와 클라우드 설정을 바꿀 수 있는 계정이 그렇습니다. 비밀번호 변경이나 출금처럼 민감한 동작 바로 앞에서 두 번째 단계를 한 번 더 묻기도 합니다.
사람이 없는 프로그램은 두 번째 단계를 넘지 못합니다. 코드를 올릴 때마다 빌드와 테스트를 자동으로 돌리는 지속적 통합, 곧 CI(Continuous Integration) 기계가 그렇습니다. 휴대폰으로 온 숫자를 대신 넣어 줄 사람이 없습니다.
그래서 이런 프로그램에는 사람의 로그인 대신 프로그램 전용 비밀 문자열을 줍니다. 대표적인 것이 API 키입니다. API(Application Programming Interface)는 프로그램이 다른 프로그램을 부르는 정해진 창구입니다. 그 창구를 부를 때 이 키를 함께 냅니다.
사람 계정에는 2단계 인증을 겁니다. 프로그램에는 권한을 좁힌 키를 줍니다.
관련 항목
2단계 인증이 속하는 상위 분류
2단계 인증과 헷갈리는 이웃
다중 인증 · 인증 요소 · 비밀번호 없는 인증 · 인증과 인가
두 번째 단계를 만드는 수단
일회용 비밀번호 · TOTP · HOTP · 푸시 알림 · 보안 키 · 패스키 · 생체 인증 · WebAuthn · FIDO2
2단계 인증이 막으려는 공격
피싱 · 크리덴셜 스터핑 · 계정 탈취 · 비밀번호 스프레이 · 무차별 대입 공격
2단계 인증을 뚫는 공격
SIM 스와핑 · 중간자 공격 · MFA 피로 공격 · 재전송 공격 · 세션 하이재킹
2단계 인증을 구현할 때 함께 다루는 기술
비밀번호 · 비밀번호 해싱 · 복구 코드 · 비밀번호 재설정 · 쿠키 · 속도 제한 · 위험 기반 인증 · API 키 · 지속적 통합
다른 이름: 2SV · two-step verification · 2-step verification · 2단계 확인