이메일 일회용 비밀번호(Email OTP) 개발
이메일 일회용 비밀번호(Email OTP)는 비밀번호로 로그인하는 GitLab.com 사용자를 위한 이중 인증(2FA) 방법입니다. 개발 특화되지 않은 이 기능에 대한 정보는 기능 문서를 참조하세요. 2026년 1월부터 GitLab.com은 Email OTP를 필수 최소 요건으로 도입하고 있습니다.
이메일 일회용 비밀번호(Email OTP)는 비밀번호로 로그인하는 GitLab.com 사용자를 위한 이중 인증(2FA) 방법입니다. 사용자는 로그인 중 이메일로 일회용 코드를 받고 인증을 완료하기 위해 입력해야 합니다.
개발 특화되지 않은 이 기능에 대한 정보는 기능 문서를 참조하세요.
2026년 1월부터 GitLab.com은 Email OTP를 필수 최소 요건으로 도입하고 있습니다. 이 기능의 개발자는 피처 플래그, GitLab 인스턴스 설정 및 미래 날짜 등록 필요성에 유의해야 합니다.
로깅#
GitLab 프로덕션 로그를 사용하여 Email OTP에서 발생하는 이슈를 트리아지하고 디버그할 수 있습니다.
로그인 중 Email OTP 이벤트#
Email OTP 검증 이벤트 쿼리:
json.message: "Email Verification" AND json.username:replace_username_here
이벤트 유형을 보려면 json.event 열을 추가합니다. 이 로그는 다음 경우에 나타납니다:
- 계정이 Email OTP를 필요로 함.
- 계정이 경고 기간에 있음(
email_otp_required_after가 7일 이하) - 계정이 잠겨 있음(기존
VerifiesWithEmail동작)
IP 주소로 검색하는 성공적인 로그인 흐름을 보여주는 로그 예시:

이벤트 이유는 VerifiesWithEmail 상수에 정의되어 있습니다.
등록 변경#
사용자 환경 설정 수정 보기:
json.meta.caller_id: "UserSettings::ProfilesController#update" AND json.params.value: "email_otp_required_as_boolean"
레코드를 펼쳐 제출되는 환경 설정 옵션을 확인합니다. 파라미터 값 1은 사용자가 Email OTP에 등록하는 것을 나타내고, 0은 등록 해제를 나타냅니다.
추가 검색#
모든 사용자 활동:
json.username: "USERNAME" OR json.meta.user: "USERNAME"
세션 이벤트:
json.controller: "SessionsController" AND json.action: (new OR create OR resend_verification_code OR successful_verification)
비밀번호 인증 가능 작업:
json.controller: (Repositories::GitHttpController OR JwtController) AND json.path: "/PROJECT_PATH"
아키텍처#
Email OTP는 이메일 검증 로직의 일부입니다. 여기에는 사용자가 새 IP 주소에서 로그인하거나 잠긴 후 로그인할 때 제공된 코드의 검증이 포함됩니다.
이것은 사용자 등록 흐름에서 발생하는 Identity Verification 기능 및 Devise의 Confirmable 기능과는 다릅니다.
코드#
컨트롤러:
SessionsController- 인증 진입점VerifiesWithEmail- 로그인 중 코드 전송 및 검증
모델:
User- Email OTP 속성을UserDetail에 위임UserDetail- Email OTP 상태 저장Users::EmailOtpEnrollment- 등록 로직 및 상태 관리
헬퍼:
VerifiesWithEmailHelper- 백엔드 및 프론트엔드 헬퍼 메서드SessionsHelper- 세션 관련 헬퍼
데이터베이스 스키마#
user_details 테이블은 다음을 저장합니다:
email_otp- 해시된 OTP 코드(사용 후 nil)email_otp_required_after- 등록 상태를 제어하는 적용 타임스탬프email_otp_last_sent_at- 마지막 코드 전달 시간email_otp_last_sent_to- 코드가 전송된 주소
상태 관리#
email_otp_required_after 값은 Users::EmailOtpEnrollment#set_email_otp_required_after_based_on_restrictions에 의해 자동으로 관리됩니다.
등록 상태에는 nil(미등록), 미래 날짜(다가오거나 현재 경고 기간) 또는 과거 날짜(적용 활성화)가 포함됩니다.
Users::UpdateService를 통해 사용자를 업데이트하면 set_email_otp_required_after_based_on_restrictions를 사용하여 상태 관리를 강제하고 값을 잠재적으로 재정의합니다. 이것은 롤아웃 목적으로 수행되었으며 향후 제거될 수 있습니다.
동일한 set_email_otp_required_after_based_on_restrictions 메서드 호출은 User#email_based_otp_required?에서도 발생합니다. 이 메서드는 사용자에게 email OTP가 필요한지 확인하기 위한 SSoT이며 email OTP 요구 사항이 적용 가능한 모든 흐름에서 사용됩니다.
이 동작은 예상된 것이며 event.message에 set_email_otp_required_after_based_on_restrictions 메서드 이름이 있는 로그를 생성합니다. 코드 주석에 상태 전환이 설명되어 있습니다.
보안#
Email OTP는 그룹 또는 인스턴스 2FA 요구 사항을 만족하지 않습니다. 앱 기반 TOTP와 WebAuthn만 이 정책을 이행합니다. 그러나 사용자에게 다른 2FA 방법이 구성되어 있지 않은 경우 앱 기반 TOTP 또는 WebAuthn 방법을 추가할 때까지 Email OTP가 필요합니다. 이 요구 사항은 보안을 위해 의도적입니다.
이메일 검증 흐름에 대한 무차별 대입 공격을 방지하기 위해 속도 제한이 존재합니다. 이 모듈은 두 가지 속도 제한을 구현합니다:
- 로그인 속도 제한 (
user_sign_in): 사용자가 올바른 비밀번호를 입력하지만 이메일 검증이 필요할 때 적용됩니다. 이는 이메일 검증 페이지가 표시될 때를 관찰하여 공격자가 비밀번호를 추측하는 것을 방지합니다. - 이메일 검증 코드 재전송 속도 제한 (
email_verification_code_send): 사용자가 검증 코드 재전송을 요청할 때 적용되어 이메일 전송 메커니즘 남용을 방지합니다. - 이메일 검증 토큰 유효성 검사 속도 제한 (
email_verification): 사용자가 검증 토큰을 제출할 때 적용되어 토큰을 추측하는 무차별 대입 시도를 방지합니다.
검증 코드는 고정 기간 후에 만료됩니다. 코드가 만료되기 전에 사용자가 검증하지 않으면 새 코드를 요청할 수 있습니다.
검증 코드는 기본 이메일 주소로 전송됩니다. 사용자에게 확인된 보조 주소가 있는 경우 거기에도 새 코드를 보낼 수 있습니다. 이 경우 기본 이메일 주소에도 보안 알림이 전송됩니다.
인증 우선순위#
이중 인증은 다음 순서로 제공됩니다:
- WebAuthn (구성된 경우)
- 앱 기반 TOTP (구성된 경우)
- Email OTP (허용되고 필요한 경우)
확인된 보조 이메일 주소를 가진 사용자는 기본 이메일 주소에 접근할 수 없는 경우 새 코드를 재전송할 수 있습니다.
구성#
피처 플래그:
email_based_mfa- Email OTP 적용을 위한 전역 토글
애플리케이션 설정:
require_minimum_email_based_otp_for_users_with_passwords- 다른 2FA가 없는 사용자에게 Email OTP를 필수로 설정하고, 비밀번호를 가진 신규 사용자를 자동으로 등록
테스트#
GitLab.com과 일치하도록 GDK 구성:
# Admin > Settings > General > New user account restrictions
ApplicationSetting.current.update!(
require_admin_approval_after_user_signup: false,
email_confirmation_setting: 'hard' )
# Admin > Settings > General > New user account restrictions
ApplicationSetting.current.update!(
anti_abuse_settings: {
require_email_verification_on_account_locked: true
}
)
아래와 같은 명령을 사용하여 등록 상태를 테스트합니다:
user = User.find_by(username: 'test_user')
# Enable Email OTP
Feature.enable(:email_based_mfa, user)
# Disable Email OTP
Feature.disable(:email_based_mfa, user)
# Require Email OTP as a minimum (also enrols new users with passwords)
ApplicationSetting.current.update!(sign_in_restrictions: {require_minimum_email_based_otp_for_users_with_passwords: true })
# Or allow users to disable it
ApplicationSetting.current.update!(sign_in_restrictions: {require_minimum_email_based_otp_for_users_with_passwords: false })
# Set enrollment date via UpdateService (triggers automatic enrollment logic)
Users::UpdateService.new( user, { user: user, email_otp_required_after: date } ).execute!
# Or set directly, bypassing set_email_otp_required_after_based_on_restrictions
user.update(email_otp_required_after: date) # nil to unenroll
https://gdk.test:3443/rails/letter_opener에서 이메일을 확인합니다.
경고 기간 단계는 코드에 정의되어 있습니다 - 임계값은 VerifiesWithEmail 및 VerifiesWithEmailHelper를 참조하세요.
QA 통합#
프로덕션 및 스테이징 엔드투엔드 테스트가 올바르게 기능하기 위해 GitLab은 요청의 User-Agent가 구성된 GITLAB_QA_USER_AGENT와 일치할 때 QA 사용자가 Email OTP를 우회할 수 있도록 합니다.
관련 코드#
테스트 파일:
spec/requests/verifies_with_email_spec.rb- Email OTP 로그인 흐름에 대한 통합 테스트spec/models/concerns/users/email_otp_enrollment_spec.rb- 등록 상태 관리에 대한 단위 테스트spec/helpers/verifies_with_email_helper_spec.rb- Email OTP 헬퍼 메서드에 대한 테스트
추가 리소스#
사용자 문서:
- 이중 인증 - Email OTP 활성화 및 로그인
- 이중 인증 문제 해결 - Email OTP를 사용한 비밀번호 API 오류
- 이중 인증 강제 - Email OTP는 2FA 요구 사항을 만족하지 않음
기술 문서:
내부 지원:
- Slack:
#mfa_default_planning
