// OTP 보안

OTP 구현 모범 사례: 코드, 만료, 속도 제한, 폴백

일회용 비밀번호는 사소해 보입니다 — 숫자를 만들어 문자로 보내고 확인하면 끝. 하지만 계정 탈취 버그는 바로 그 세부에 숨어 있습니다. 이 가이드는 OTP 플로우를 진정으로 안전하고 쓰기 좋게 만드는 결정들 — 코드 엔트로피, 저장, 만료 시간, 속도 제한, 재시도, 폴백 채널 — 을 다룹니다.

진짜 엔트로피로 코드 생성

암호학적으로 안전한 난수 생성기를 사용하고, 예측 가능한 시드나 타임스탬프에서 파생한 값은 절대 쓰지 마세요. 여섯 자리가 보안과 사용성의 일반적 균형점이며, 더 짧은 코드는 안전을 위해 더 엄격한 속도 제한이 필요합니다.

같은 코드를 두 번 보내지 말고, 사용자 ID나 순번에서 코드를 유추할 수 있게 만들지 마세요. SMS OTP의 보안 전체가 짧은 수명 안에서 코드가 예측 불가능하다는 데 달려 있습니다.

  • 범용 PRNG가 아니라 CSPRNG(예: secrets/crypto 라이브러리)를 사용하세요.
  • 6자리가 표준이며, 4자리를 쓴다면 속도 제한과 만료를 강하게 조이세요.
  • 요청당 하나의 코드; 새 코드를 발급할 때 이전 코드를 무효화하세요.

코드를 안전하게 저장, 절대 평문 금지

OTP를 수명이 짧은 비밀번호처럼 다루세요. 원시 값이 아니라 (식별자와 만료를 포함한) 코드의 해시를 저장해 데이터베이스 유출이 유효한 코드를 노출하지 않게 하세요. 타이밍 공격을 피하기 위해 상수 시간 함수로 비교하고, 사용되거나 만료되는 즉시 레코드를 삭제하세요.

  • 저장 전 코드를 해시하고, 상수 시간으로 비교하세요.
  • 각 코드를 단일 사용자/세션과 목적에 결속하세요.
  • 성공, 만료, 또는 시도 한도 도달 시 삭제하세요.

짧은 만료 시간 설정

코드는 시간이 아니라 분 단위로 살아 있어야 합니다. 5~10분 만료가 일반적 범위입니다: 지연된 SMS가 아직 도착할 만큼 길고, 유출된 코드가 대개 이미 죽었을 만큼 짧습니다. 느린 메시지가 고장 난 플로우처럼 보이지 않도록 남은 유효 시간을 사용자에게 표시하세요.

!

짧은 만료를 재전송 쿨다운과 함께 두세요. 그렇지 않으면 메시지가 지연될 때 사용자가 재전송을 연타해 SMS 비용을 배로 늘립니다.

모든 차원에서 속도 제한

속도 제한은 코드의 무차별 대입 추측과 발송 엔드포인트 남용(비용을 발생시키고 발신자가 플래그될 수 있음)을 모두 막는 장치입니다. 발송과 확인 모두에서 전화번호별, IP별, 계정별로 제한하세요.

  • 확인 시도: 코드당 시도 횟수(예: 5회)를 제한한 뒤 무효화하고 재전송을 강제하세요.
  • 발송 요청: 발송 간 쿨다운과 번호당 일일 한도를 두세요.
  • 전역 가드: 자동화 남용을 무디게 하는 IP별·계정별 한도.
  • 한도가 반복적으로 걸리면 지수 백오프나 CAPTCHA를 추가하세요.

재시도와 폴백 채널 처리

SMS는 최선 노력 방식이므로 좋은 플로우는 일부 메시지가 도착하지 않으리라 전제합니다. 쿨다운 후 재전송을 제공하고, 한두 번의 SMS 실패 후에는 폴백 채널 — 코드를 읽어 주는 음성 통화, WhatsApp, 이메일 — 로 에스컬레이션하세요. 이것이 바로 관리형 Verify API가 자동화하는 것입니다.

실패 상태를 인간적으로 만드세요: 명확한 "못 받으셨나요?" 경로, 번호를 바꿀 방법, 반복 실패 후 지원 연락 수단.

  1. 1

    첫 시도: SMS

    코드를 보내고 재전송 쿨다운과 함께 보이는 카운트다운을 시작하세요.

  2. 2

    요청 시 재전송

    쿨다운 후 재전송을 허용하고, 새 코드를 발급할 때 이전 코드를 무효화하세요.

  3. 3

    채널 에스컬레이션

    반복된 SMS 실패 후 같은 인증을 전달할 음성이나 대체 채널을 제공하세요.

  4. 4

    출구 제공

    막다른 길 대신 사용자가 번호를 고치거나 지원에 닿게 하세요.

보안을 해치지 않으면서 UX 개선

작은 손질이 완료율을 높입니다: 모바일 브라우저가 코드를 읽도록 WebOTP API와 SMS 자동완성 형식을 사용하고, 메시지 문구를 일관되게 유지하며(일부 플랫폼은 신뢰 발신자를 캐싱), 메시지에 코드와 앱 이름 외에는 아무것도 넣지 마세요. OTP 메시지에 링크를 피하세요 — 사용자가 클릭하도록 길들이고 통신사 스팸 필터에 걸립니다.

  • 모바일에서 WebOTP / 일회용 코드 자동완성을 지원하세요.
  • 메시지를 최소로: 앱 이름 + 코드, 마케팅 없음, 링크 없음.
  • 메시지 텍스트는 현지화하되 코드 형식은 안정적으로 유지하세요.

자주 묻는 질문

OTP는 얼마나 오래 유효해야 하나요?+

5분에서 10분이 일반적 범위입니다. 지연된 SMS를 견딜 만큼 길고 유출된 코드가 대개 이미 만료됐을 만큼 짧습니다. 느린 메시지가 고장 난 플로우처럼 보이지 않도록 항상 남은 시간을 표시하세요.

확인 시도는 몇 번까지 허용해야 하나요?+

코드당 시도를 약 5회로 제한한 뒤 코드를 무효화하고 새 발송을 요구하세요. 여기에 발송·확인 모두에 대한 번호별·IP별·계정별 속도 제한을 결합해 무차별 대입과 발송 엔드포인트 남용을 막으세요.

OTP 메시지에 링크를 넣어야 하나요?+

아니요. OTP 메시지는 앱 이름과 코드만으로 유지하세요. 링크는 사용자가 클릭하도록 길들이고, 리퍼러를 통해 코드를 유출할 수 있으며, 전달률을 해치는 통신사 스팸 필터링을 자주 유발합니다.

SMS OTP만으로 충분히 안전한가요?+

SMS OTP는 비밀번호만 쓰는 것보다 의미 있는 향상이지만 SIM 스왑과 가로채기에 취약합니다. 고가치 계정에는 패스키나 TOTP 같은 더 강한 요소를 제공하고 SMS는 폴백으로 다루세요 — OTP 대안 가이드를 참고하세요.

EdgeGigs

OTP 플로우, 처음부터 제대로 구현하세요.

EdgeGigs의 검증된 개발자가 안전한 코드 생성, 만료, 속도 제한, 폴백까지 여러분의 웹·모바일 앱에 구현해 드립니다.

EdgeGigs에서 개발자 찾기