일회용 비밀번호
고친 사람 github-actions[bot]
일회용 비밀번호는 로그인할 때 한 번만 통하는 비밀번호입니다. 한 번 쓰고 나면 버려지므로 누가 엿봐도 다시 쓸 수 없습니다. 휴대폰 문자로 받은 여섯 자리 숫자나 인증 앱에 잠깐 떴다 사라지는 숫자가 흔한 모습입니다.
쉽고 빠른 이해
일회용 비밀번호는 한 번 쓰면 끝나는 짧은 숫자입니다. 로그인하다 문자로 받은 인증번호를 넣어 봤다면 그것이 일회용 비밀번호입니다.
늘 같은 비밀번호는 한 번 새면 계속 쓰입니다. 값이 매번 바뀌면 엿본 값은 다음 로그인에 소용이 없습니다.
어떻게 도나:
- 서버와 사용자 쪽이 같은 숫자를 얻는 방법을 미리 맞춰 둡니다. 서버가 문자로 보내 주거나, 둘이 나눠 가진 비밀 값으로 각자 계산합니다
- 사용자가 받은 숫자를 로그인 화면에 넣습니다
- 서버가 자기 쪽 숫자와 맞춰 봅니다. 맞으면 그 숫자를 다시 못 쓰게 합니다
대가는 로그인에 한 단계가 더 붙는다는 것입니다. 휴대폰이 없으면 들어갈 수 없습니다. 가짜 사이트가 입력받은 숫자를 곧바로 가져가 쓰는 공격도 못 막습니다.
상세
공연장 입구에서는 표를 받으면 한 귀퉁이를 찢습니다. 찢긴 표로는 다시 들어갈 수 없습니다. 누가 그 표를 주워 가도 마찬가지입니다.
비밀번호는 본인만 아는 문자열로 본인임을 보이는 수단입니다. 보통의 비밀번호는 사용자가 바꾸기 전까지 늘 같습니다. 일회용 비밀번호(OTP, One-Time Password)는 한 번 통하면 버려지는 비밀번호입니다. 값은 로그인할 때마다 바뀝니다. 흔히 인증번호나 인증 코드라고도 부릅니다.
늘 같은 비밀번호의 약점
늘 같은 비밀번호는 한 번 새면 끝입니다. 어깨 너머로 엿보든, 다른 사이트에서 새어 나오든 값 하나만 얻으면 됩니다. 얻은 쪽은 주인이 비밀번호를 바꿀 때까지 몇 번이고 들어옵니다.
엿들은 값을 나중에 다시 보내 통과하는 공격을 재전송 공격이라고 부릅니다. 값이 늘 같으면 이 공격을 막을 방법이 없습니다. 서버 눈에는 주인이 보낸 값과 똑같기 때문입니다.
일회용 비밀번호는 이 약점을 없애려고 씁니다. 값이 매번 바뀌므로 엿본 값은 이미 쓰였거나 곧 효력을 잃습니다. 새어 나가도 잃는 것이 그 한 번에서 그칩니다.
같은 숫자를 얻는 두 방법
값이 매번 바뀌면 새 문제가 생깁니다. 서버와 사용자가 이번 숫자가 무엇인지 둘 다 알아야 합니다. 그 숫자를 맞추는 방법은 둘입니다. 아래 표가 둘을 나란히 놓습니다.
| 서버가 보내는 방식 | 양쪽이 계산하는 방식 | |
|---|---|---|
| 숫자를 만드는 쪽 | 서버 | 서버와 사용자 기기가 각자 |
| 사용자에게 가는 길 | 문자 메시지 · 이메일 | 없다. 기기 화면에 뜬다 |
| 미리 맞춰 둘 것 | 전화번호나 이메일 주소 | 둘이 나눠 가진 비밀 값 |
| 숫자를 얻을 때 통신 | 필요하다 | 필요 없다 |
표에서 갈리는 것은 숫자가 오가느냐입니다. 보내는 방식은 숫자가 통신망을 지나갑니다. 계산하는 방식은 숫자가 어디로도 가지 않습니다. 아래 두 소절이 두 방식을 하나씩 봅니다.
서버가 만들어 보내는 방식
서버가 무작위 숫자를 하나 만들어 사용자에게 보냅니다. 받는 길은 대개 SMS(Short Message Service, 휴대폰 문자 서비스)나 이메일입니다. 서버는 보낸 숫자를 짧은 유효 시간과 함께 적어 둡니다.
sequenceDiagram
participant 사용자
participant 서버
participant 휴대폰
사용자->>서버: 아이디와 비밀번호
서버->>서버: 무작위 숫자를 만들어 유효 시간과 함께 적어 둔다
서버->>휴대폰: 문자로 숫자를 보낸다
휴대폰-->>사용자: 화면에 숫자가 뜬다
사용자->>서버: 받은 숫자를 넣는다
서버->>서버: 적어 둔 숫자와 맞춰 보고 지운다
서버-->>사용자: 로그인 완료
그림의 마지막 단계에서 서버는 맞춰 본 숫자를 지웁니다. 입구에서 표를 찢는 것과 같습니다. 이 단계가 「일회용」을 만듭니다. 지우지 않으면 같은 숫자로 한 번 더 들어올 수 있습니다.
이 방식은 사용자 쪽에 따로 준비가 필요 없습니다. 문자를 받을 휴대폰만 있으면 됩니다. 대신 숫자가 전화망이나 메일함을 지나갑니다. 그 길을 가로챈 쪽도 숫자를 받습니다.
휴대폰 안에는 가입자를 알아보는 작은 카드가 꽂혀 있습니다. 이 카드를 SIM(Subscriber Identity Module, 가입자 식별 모듈)이라고 부릅니다. 통신사는 전화번호를 이 카드에 묶어 둡니다.
공격자가 통신사를 속여 남의 번호를 자기 SIM 으로 옮기면 그 번호로 가는 문자가 공격자에게 갑니다. 이 공격을 SIM 스와핑이라고 부릅니다. 문자로 받는 일회용 비밀번호는 이 공격 한 번에 넘어갑니다.
양쪽이 각자 계산하는 방식
두 번째 방식은 숫자를 보내지 않습니다. 서버와 사용자 기기가 같은 재료로 같은 계산을 해서 같은 숫자를 얻습니다. 이 소절은 그 재료와 계산을 봅니다.
재료는 둘입니다. 하나는 둘이 처음에 한 번 나눠 가진 비밀 값입니다. 이 값을 공유 비밀이라고 부릅니다.
다른 하나는 로그인할 때마다 바뀌는 값입니다. 공유 비밀만 넣으면 결과가 늘 같습니다. 바뀌는 값을 섞어야 매번 다른 숫자가 나옵니다.
계산의 바탕은 해시 함수입니다. 해시 함수는 입력을 섞어 정해진 길이의 값으로 바꾸는 함수입니다. 결과에서 입력을 거꾸로 알아낼 수 없습니다. 입력이 조금만 달라도 결과가 크게 달라집니다.
그 위에 HMAC(Hash-based Message Authentication Code, 해시 기반 메시지 인증 코드)을 씁니다. HMAC 은 비밀 값을 섞어서 해시하는 계산입니다. 비밀 값을 모르는 쪽은 같은 결과를 만들 수 없습니다. 그래서 숫자를 맞힌 사람은 공유 비밀이 든 기기를 쥐고 있다고 볼 수 있습니다.
HMAC 의 결과는 길어서 사람이 옮겨 적기 어렵습니다. 그래서 결과에서 일부를 떼어 여섯 자리쯤의 숫자로 줄입니다.
flowchart TD
subgraph 기기["사용자 기기"]
A1["공유 비밀"] --> C1["HMAC 계산"]
B1["바뀌는 값"] --> C1
C1 --> D1["여섯 자리로 줄인 숫자"]
end
subgraph 서버측["서버"]
A2["공유 비밀"] --> C2["HMAC 계산"]
B2["바뀌는 값"] --> C2
C2 --> D2["여섯 자리로 줄인 숫자"]
end
D1 -->|사용자가 넣는다| E{"두 숫자가 같은가"}
D2 --> E
그림에서 기기와 서버는 서로 통신하지 않습니다. 같은 재료를 넣었으니 같은 숫자가 나올 뿐입니다. 사용자가 옮겨 넣는 숫자 하나만 둘 사이를 지나갑니다.
바뀌는 값으로 쓰는 카운터와 시각
바뀌는 값으로는 두 가지를 씁니다. 하나씩 올라가는 수와 현재 시각입니다. 둘은 기기와 서버가 서로 어긋나는 모습이 다릅니다.
숫자를 새로 뽑을 때마다 하나씩 올라가는 수를 카운터라고 부릅니다. 카운터를 바뀌는 값으로 쓰는 방식이 HOTP(HMAC-based One-Time Password, HMAC 기반 일회용 비밀번호)입니다. 이 방식은 버튼 하나 달린 작은 열쇠고리 모양 기기에서 흔히 씁니다.
그 기기는 버튼을 누를 때마다 기기 쪽 카운터를 하나 올립니다. 그러면 새 숫자가 나옵니다. 서버도 로그인이 통과할 때마다 자기 쪽 카운터를 올립니다.
이 방식은 두 수가 어긋나기 쉽습니다. 사용자가 버튼만 누르고 숫자를 넣지 않으면 기기 쪽 카운터만 앞서 갑니다. 그래서 서버는 자기 카운터보다 몇 칸 앞까지 미리 계산해 봅니다. 맞는 칸을 찾으면 자기 카운터를 거기로 옮깁니다.
현재 시각을 쓰는 방식을 TOTP(Time-based One-Time Password, 시간 기반 일회용 비밀번호)라고 부릅니다. 시각을 몇십 초 단위로 끊어 구간 번호를 매깁니다. 그 번호를 바뀌는 값으로 넣습니다.
같은 구간 안에서는 기기와 서버가 같은 숫자를 얻습니다. 인증 앱에서 숫자 옆에 줄어드는 막대가 그 구간의 남은 시간입니다.
이 방식은 두 시계가 맞아야 합니다. 시계가 조금 어긋나거나, 사용자가 숫자를 넣는 사이에 구간이 넘어갈 수 있습니다. 그래서 서버는 현재 구간 앞뒤로 한 구간쯤의 숫자까지 받아 줍니다.
| 카운터 방식(HOTP) | 시각 방식(TOTP) | |
|---|---|---|
| 바뀌는 값 | 누를 때마다 오르는 수 | 몇십 초마다 바뀌는 구간 번호 |
| 숫자의 수명 | 쓰기 전까지 유효하다 | 구간이 끝나면 효력을 잃는다 |
| 어긋나는 원인 | 누르고 안 쓴 횟수 | 두 시계의 차이 |
| 서버가 받아 주는 폭 | 앞으로 몇 칸 | 앞뒤로 한 구간쯤 |
표에서 갈리는 것은 숫자의 수명입니다. 카운터 방식의 숫자는 쓰기 전까지 살아 있어서 엿본 숫자가 오래 쓸모 있습니다. 시각 방식의 숫자는 구간이 끝나면 저절로 죽습니다. 휴대폰 인증 앱은 대개 시각 방식을 씁니다.
공유 비밀을 나눠 갖는 등록
계산하는 방식은 처음에 한 번 공유 비밀을 건네야 합니다. 이 단계를 등록이라고 부릅니다. 한 번 건넨 뒤로 기기는 통신 없이 숫자를 만듭니다.
건넬 때는 대개 QR 코드를 씁니다. QR(Quick Response) 코드는 카메라로 찍어 읽는 네모난 무늬입니다. 서버가 공유 비밀을 이 무늬에 담아 화면에 띄우면 인증 앱이 찍어서 읽습니다.
대가는 공유 비밀이 서버에 원래 값 그대로 남는다는 것입니다. 비밀번호는 해시만 저장해 두고 들어온 값을 해시해 맞춰 보면 됩니다. 공유 비밀은 계산에 원래 값이 들어가야 하므로 해시로 바꿔 둘 수 없습니다.
그래서 서버 저장소가 새면 공격자가 사용자와 똑같은 숫자를 만들어 냅니다. 서버는 공유 비밀을 암호화해 보관합니다. 새었다고 의심되면 등록을 새로 받습니다.
서버가 검증할 때 챙기는 것
숫자를 맞춰 보는 일 자체는 비교 한 번입니다. 그 둘레에서 서버가 챙길 것이 셋 있습니다. 안 챙기면 일회용이라는 성질이 무너집니다.
| 챙길 것 | 안 챙기면 |
|---|---|
| 한 번 통과한 숫자를 다시 못 쓰게 막는다 | 같은 구간 안에서 엿본 숫자로 한 번 더 들어온다 |
| 틀린 시도 횟수를 제한한다 | 숫자를 차례로 다 넣어 보는 공격에 뚫린다 |
| 유효 시간을 짧게 둔다 | 엿본 숫자가 오래 쓸모 있다 |
첫째는 시각 방식에서 특히 챙깁니다. 한 구간 동안은 같은 숫자가 계속 맞기 때문입니다. 서버는 마지막으로 통과한 구간 번호를 적어 둡니다. 들어온 숫자가 맞는 구간이 그 번호 이하면 거절합니다.
둘째는 숫자가 짧아서 생기는 문제입니다. 여섯 자리 숫자는 백만 가지뿐입니다. 무차별 대입 공격은 가능한 값을 차례로 전부 넣어 보는 공격입니다.
시도 횟수에 제한이 없으면 이 공격이 통합니다. 그래서 몇 번 틀리면 잠시 막거나 숫자를 새로 받게 합니다. 속도 제한은 정해진 시간 안의 시도 횟수를 묶어 두는 장치입니다.
일회용 비밀번호가 못 막는 공격
피싱은 진짜처럼 꾸민 가짜 사이트에 사용자가 스스로 비밀 값을 넣게 만드는 공격입니다. 일회용 비밀번호도 이 공격 앞에서는 새어 나갑니다.
가짜 사이트는 받은 비밀번호와 숫자를 곧바로 진짜 사이트에 넣습니다. 숫자가 효력을 잃기 전이므로 통과합니다. 이렇게 사용자와 진짜 사이트 사이에 끼어들어 오가는 값을 넘겨받는 공격을 중간자 공격이라고 부릅니다.
사람이 숫자를 눈으로 보고 옮겨 넣는 한 이 틈은 남습니다. 숫자 안에는 어느 사이트에 넣어야 하는지가 담겨 있지 않기 때문입니다. 사이트 주소까지 확인해 이 틈을 막는 수단으로 패스키가 있습니다. 브라우저에서 패스키를 다룰 때 쓰는 규격이 WebAuthn(Web Authentication)입니다.
쓰는 곳과 안 맞는 곳
일회용 비밀번호는 대개 혼자 쓰지 않습니다. 비밀번호 다음에 두 번째 확인으로 붙습니다. 서로 다른 종류의 증거를 둘 이상 받아 본인인지 가리는 방식을 다중 인증이라고 부릅니다.
다중 인증에서 비밀번호는 「아는 것」입니다. 일회용 비밀번호를 받는 휴대폰은 「가진 것」입니다. 비밀번호가 새어도 휴대폰까지 빼앗기지 않았으면 들어올 수 없습니다.
비밀번호 없이 일회용 비밀번호만으로 들여보내는 서비스도 있습니다. 이메일로 숫자를 보내 주는 로그인이 그렇습니다. 이때는 그 메일함을 가진 사람이 곧 주인으로 인증됩니다.
사람이 끼지 않는 곳에는 맞지 않습니다. 일회용 비밀번호는 사람이 숫자를 옮겨 넣는 것을 전제로 합니다. 서버끼리 주고받는 요청에는 옮겨 넣을 사람이 없습니다.
서버끼리는 서로의 기능을 API(Application Programming Interface)로 불러 씁니다. API 는 프로그램이 다른 프로그램의 기능을 불러 쓰는 통로입니다. 이런 호출에는 API 키나 액세스 토큰처럼 프로그램이 들고 다니는 값을 씁니다.
관련 항목
일회용 비밀번호를 만드는 계산 방식
HOTP · TOTP · HMAC · 해시 함수 · 공유 비밀 · 난수 생성기 · 해시 체인
일회용 비밀번호를 사용자에게 건네는 경로
SMS · 이메일 · 인증 앱 · 하드웨어 토큰 · 푸시 알림 · QR 코드
일회용 비밀번호가 속하는 상위 분류
자격증명 · 인증 · 인증과 인가 · 다중 인증 · 인증 수단 · 비밀번호 없는 인증
일회용 비밀번호를 노리는 공격
피싱 · 중간자 공격 · 재전송 공격 · 무차별 대입 공격 · SIM 스와핑 · 사회공학
일회용 비밀번호를 대신할 수 있는 다른 수단
패스키 · WebAuthn · FIDO2 · 보안 키 · 생체 인증 · 비밀번호
일회용 비밀번호를 검증하는 서버가 쓰는 장치
다른 이름: OTP · one-time password · One-Time Password · one-time passcode · 일회용 패스워드 · 원타임 패스워드 · 일회용 코드