본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Backend

2단계 인증: TOTP와 패스키

작성 2026. 10. 21.추가
목차6개 항목
출처와 참고

ByteByteGo의 System Design 101 가이드 「How Google Authenticator Works」, 「Is PassKey Shaping a Passwordless Future?」를 바탕으로, TOTP 규격(RFC 6238)과 WebAuthn 명세로 확인해 재구성한 노트다. 「인증 요소」 절의 요소별 약점은 CrashCourse의 「Crash Course Computer Science」 강의를 바탕으로 보강했다.

인증 요소

인증은 세 종류의 증거로 한다. 서로 다른 종류를 두 개 이상 요구하면 다중 요소 인증(MFA)이다. 비밀번호 두 개는 같은 종류라 MFA가 아니다.

요소의미예약점
아는 것기억하는 비밀비밀번호, PIN추측·대입·유출된다. 4자리 PIN은 1만 가지뿐이라 시도 횟수를 막아도, 봇넷이 수만 계정에 흔한 PIN 하나씩만 넣어 보면 몇 개는 열린다
가진 것소유한 기기·키인증기 앱이 든 휴대폰, 보안 키, 패스키가 저장된 기기추측할 수 없고 원격 공격자가 손에 넣기 어렵다. 대신 가까이 있는 사람에게 도난·복제당한다
나 자신생체 정보지문, 얼굴판정이 확률적이다. 본인을 거부하거나(조명·모자) 남을 본인으로 받아들일 확률이 0이 아니다. 유출돼도 바꿀 수 없다

아는 것과 가진 것은 맞거나 틀리거나 둘 중 하나로 판정되지만, 생체는 센서 값이 매번 달라 임계값으로 판정한다. 그래서 패스키처럼 생체를 기기 잠금 해제에만 쓰고, 서버에는 생체 정보를 보내지 않는 구조가 안전하다.

TOTP: 시간 기반 일회용 비밀번호

Google Authenticator 같은 인증기 앱의 6자리 숫자는 TOTP(Time-based One-Time Password, RFC 6238) 다. 서버와 앱이 같은 비밀 키를 나눠 갖고, 각자 현재 시각으로 같은 숫자를 계산한다. 네트워크로 코드를 받는 것이 아니므로 앱은 오프라인에서도 동작한다.

1단계: 등록

sequenceDiagram
  participant U as 사용자
  participant S as 서버
  participant D as 화면
  participant A as 인증기 앱
  U->>S: 2단계 인증 켜기
  Note over S: 비밀 키 생성 → 사용자 계정에 (암호화해) 저장
  S->>D: otpauth://totp/발급자:사용자?secret=...&issuer=... 를 QR 코드로 표시
  A->>D: QR 스캔
  Note over A: 비밀 키를 기기에 저장
  U->>S: 앱에 뜬 코드를 한 번 입력해 등록 확인

2단계: 로그인

sequenceDiagram
  participant A as 앱
  participant U as 사용자
  participant S as 서버
  Note over A: 코드 = HOTP(비밀 키, floor(현재 Unix 시각 / 30))
  A-->>U: 6자리 표시
  U->>S: 비밀번호 + 코드 입력
  Note over S: 저장된 비밀 키로 같은 계산 → 일치하면 통과
  • HOTP는 비밀 키와 카운터로 HMAC을 계산해 6자리로 자른 것이고, TOTP는 그 카운터를 30초 단위 시각으로 바꾼 것이다.
  • 기기 시계가 조금 어긋나도 통과하도록 서버는 보통 앞뒤 한 구간 정도를 함께 허용한다.

안전한가

질문답
비밀 키가 새면?누구나 코드를 만들 수 있다. 등록 화면은 HTTPS로만, 서버는 비밀 키를 암호화해 저장한다. 해시로는 저장할 수 없다(계산에 원본이 필요하다)
6자리를 맞힐 수 있나?경우의 수는 100만이고 30초마다 바뀐다. 그래도 시도 횟수 제한이 없으면 의미가 줄어든다. 실패 횟수로 잠그고, 이미 쓴 코드는 다시 받지 않는다
피싱은?막지 못한다. 가짜 사이트가 비밀번호와 코드를 받아 30초 안에 진짜 사이트에 넣으면 통과한다

SMS 코드는 번호 이동·유심 복제 공격에 약해 TOTP보다 약한 선택지다.

패스키: 비밀을 공유하지 않는 로그인

패스키(passkey) 는 FIDO2·WebAuthn 표준의 공개키 자격 증명이다. 비밀번호와 TOTP는 모두 서버와 사용자가 같은 비밀을 나눠 갖는다는 약점이 있다. 패스키는 서버에 공개키만 둔다.

등록

sequenceDiagram
  participant S as 서버
  participant B as 브라우저
  participant D as 기기(인증기)
  S->>B: 챌린지(난수), 사이트 ID(rp.id = example.com)
  Note over D: 생체·PIN으로 사용자 확인 → 이 사이트 전용 키 쌍 생성
  D->>S: 공개키 + 자격 증명 ID (개인키는 기기 밖으로 나가지 않는다)

로그인

sequenceDiagram
  participant S as 서버
  participant B as 브라우저
  participant D as 기기(인증기)
  S->>B: 새 챌린지
  Note over D: 생체·PIN으로 잠금 해제 → 개인키로 (챌린지 + 출처 정보) 서명
  Note over S: 저장된 공개키로 서명 검증 → 로그인
성질의미
서버에 비밀이 없다DB가 유출돼도 공개키뿐이라 로그인에 쓸 수 없다
피싱에 강하다브라우저가 서명에 실제 도메인을 넣는다. 가짜 도메인에서는 그 사이트용 키가 아예 쓰이지 않는다
재사용 불가매번 새 챌린지에 서명하므로 가로챈 응답을 다시 써도 소용없다
한 번에 두 요소기기(가진 것) + 생체·PIN(나 자신·아는 것)
  • 키 쌍은 기기가 만든다. 서버가 주는 것은 챌린지와 사이트 정보뿐이다.
  • 동기화 패스키는 iCloud 키체인·Google 비밀번호 관리자 같은 계정 저장소를 통해 사용자의 여러 기기에 퍼진다. 기기 결합 패스키는 보안 키처럼 한 기기에만 있다. 동기화는 편의를, 기기 결합은 통제를 얻는다.
  • 다른 기기의 패스키는 QR 코드를 찍고 블루투스로 근접을 확인하는 교차 기기 인증으로 쓴다.
  • 웹 쪽 구현(자동 완성 UI, autocomplete="username webauthn")은 로그인·가입 폼과 인증 입력에 있다.

고르는 기준

방식피싱 저항서버 유출 시사용자 부담
비밀번호만없다해시 대입 위험기억
비밀번호 + SMS없다해시 대입 위험문자 대기, 유심 공격
비밀번호 + TOTP없다TOTP 비밀 키까지 새면 위험앱에서 코드 옮기기
패스키있다공개키뿐이라 안전생체·PIN 한 번

정리

  • MFA는 서로 다른 종류의 증거를 요구한다.
  • TOTP는 공유한 비밀 키 + 현재 시각으로 양쪽이 같은 코드를 계산한다. 시도 횟수 제한과 비밀 키 암호화 저장이 필수이고, 피싱은 막지 못한다.
  • 패스키는 사이트마다 기기에서 만든 키 쌍으로 챌린지에 서명한다. 서버에 비밀이 없고 도메인에 묶여 피싱에 강하다.
  • 새로 만드는 서비스는 패스키를 기본으로, TOTP를 대안으로 둔다.

관련 노트: 로그인·가입 폼과 인증 입력, 비밀번호 저장과 비밀번호 관리자, 인코딩·암호화·해시·토큰화와 디지털 서명, 세션 기반 인증과 토큰 기반 인증, OAuth 2.0

원본 기록5

댓글

GitHub 계정으로 댓글을 남길 수 있어요.