Add 휴대폰 번호 단독 로그인 기술 검토 및 구현 검토.md
This commit is contained in:
477
휴대폰 번호 단독 로그인 기술 검토 및 구현 검토.md
Normal file
477
휴대폰 번호 단독 로그인 기술 검토 및 구현 검토.md
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
# 휴대폰 번호 단독 로그인 기술 검토 및 구현 대안
|
||||||
|
|
||||||
|
작성일: 2026-06-23
|
||||||
|
|
||||||
|
## 1. 목적
|
||||||
|
|
||||||
|
본 문서는 Baron SSO에서 논의된 "휴대폰 번호만 입력하면 즉시 로그인되는 방식"의 기술 구현안을 검토하고, 각 프로세스에서 어떤 데이터가 생성되는지, 해당 단계가 표준 기술에 해당하는지, 비표준 또는 우회 구현이라면 어떤 위험이 있는지 정리한다.
|
||||||
|
|
||||||
|
검토 범위는 다음 이슈와 현재 코드 기준 구현 흔적을 포함한다.
|
||||||
|
|
||||||
|
- Gitea Issue #1241: `[Feature/API] Headless 폰번호 전용 로그인 API 구현`
|
||||||
|
- Gitea Issue #1245: `[Research/Design] 기존 QR코드/링크 로그인 아키텍처 분석 및 Headless 폰번호 로그인 적용 설계안`
|
||||||
|
- Gitea Issue #1247: `[Architecture/Design] Baron Backend ↔ Ory Kratos 인터랙션 분석 및 폰번호 로그인 세션 발급 아키텍처`
|
||||||
|
- Gitea Issue #1248: `[Feature] 휴대폰 번호 입력만으로 즉시 로그인 기능 구현 (인증 코드 없는 Passwordless)`
|
||||||
|
- Gitea Issue #1252: `[Feature] userfront 내 휴대폰 번호 입력 전용 초간결 로그인 화면/모드 구현`
|
||||||
|
- Gitea Issue #1258: `[Feature] userfront 내 휴대폰 번호 전용 독립 라우트(connect) 신설 및 비인가/로그아웃 리다이렉션 전면 일괄 전환`
|
||||||
|
|
||||||
|
## 2. 결론 요약
|
||||||
|
|
||||||
|
휴대폰 번호만으로 로그인시키는 방식은 사용자 인증 관점에서 표준 로그인 방식으로 보기 어렵다. 휴대폰 번호는 사용자 식별자(identifier)일 수는 있지만, 그 자체로 사용자 본인성 또는 번호 소유를 증명하는 인증 수단(authenticator)이 아니다.
|
||||||
|
|
||||||
|
현재 논의된 구현안은 크게 세 가지로 나뉜다.
|
||||||
|
|
||||||
|
| 방식 | 요약 | 표준성 판단 | 권장 여부 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Kratos Admin 세션 직접 발급 | 번호로 사용자를 찾은 뒤 관리자 API로 세션 생성 | 제품 내부 Admin API 사용일 수 있으나 사용자 인증 우회 | 비권장 |
|
||||||
|
| Courier 인터셉트/코드 자동 제출 | Kratos code login을 시작하고 발송 코드를 백엔드가 가로채 제출 | Kratos code login 자체는 표준 플로우이나 코드 소유자 확인을 제거한 우회 | 비권장 |
|
||||||
|
| Headless client assertion + 폰번호 | RP 클라이언트 JWT는 검증하고 사용자는 번호만 입력 | 클라이언트 인증은 표준 JWT 기반일 수 있으나 사용자 인증은 부재 | 제한적/보완 필수 |
|
||||||
|
|
||||||
|
보완 없이 운영하면 전화번호를 아는 사람 또는 내부 시스템 접근자가 타인의 세션을 만들 수 있는 구조가 된다. 따라서 최소한 SMS OTP, FIDO2/WebAuthn, 기기 바인딩, 사전 등록 단말 인증, 관리자 승인, 네트워크 제한 중 하나 이상의 실질적인 사용자 인증 또는 환경 인증이 필요하다.
|
||||||
|
|
||||||
|
## 3. 현재 코드 기준 상태
|
||||||
|
|
||||||
|
현재 코드에서 확인되는 관련 위치는 다음과 같다.
|
||||||
|
|
||||||
|
| 영역 | 파일 | 확인 내용 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Backend route | `backend/cmd/server/main.go` | `/api/v1/auth/headless/link/init`, `/api/v1/auth/headless/link/poll` 등록 |
|
||||||
|
| Headless link init/poll | `backend/internal/handler/auth_handler.go` | `HeadlessLinkInit`, `HeadlessLinkPoll`, `startHeadlessPhoneLink` 구현 |
|
||||||
|
| Kratos code login 시작 | `backend/internal/service/ory_service.go` | `InitiateLinkLogin`에서 Kratos code login flow 시작 |
|
||||||
|
| Kratos code 제출 | `backend/internal/service/ory_service.go` | `VerifyLoginCode`에서 `self-service/login?flow=...`에 code 제출 |
|
||||||
|
| Courier relay | `backend/internal/handler/auth_handler.go` | `HandleKratosCourierRelay`에서 login code를 Redis에 저장하거나 QR 흐름을 자동 검증 |
|
||||||
|
| Admin 세션 직접 발급 함수 | `backend/internal/handler/auth_handler.go` | `issueKratosSession` 함수는 있으나 현재 검색 기준 호출부는 없음 |
|
||||||
|
| OryProvider IssueSession | `backend/internal/service/ory_service.go` | `IssueSession`은 `domain.ErrNotSupported` 반환 |
|
||||||
|
| Userfront phone-only route | `userfront/lib/main.dart`, `userfront/lib/features/auth/presentation/login_screen.dart` | 현재 검색 기준 `connect`, `phone_only`, `phone-login` 직접 구현은 확인되지 않음 |
|
||||||
|
|
||||||
|
따라서 이슈 #1248에서 제안된 `POST /api/v1/auth/phone-login` 직접 API는 현재 코드상 활성 라우트로 확인되지 않는다. 실제로 가까운 구현은 `/api/v1/auth/headless/link/init` 및 `/poll` 기반의 headless link 흐름이다.
|
||||||
|
|
||||||
|
## 4. 프로세스별 데이터 생성 및 표준성 검토
|
||||||
|
|
||||||
|
### 4.1 휴대폰 번호 입력 및 정규화
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. 사용자가 휴대폰 번호를 입력한다.
|
||||||
|
2. Userfront 또는 Backend가 하이픈, 공백 등을 제거한다.
|
||||||
|
3. Backend가 `normalizePhoneForLoginID` 계열 로직으로 E.164에 가까운 로그인 식별자로 변환한다.
|
||||||
|
4. 변환된 값으로 Kratos identity 또는 로컬 원장을 조회한다.
|
||||||
|
|
||||||
|
생성 또는 변환되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 예시 | 생성 주체 | 용도 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Raw phone number | `010-1234-5678` | 사용자/Userfront | 입력값 |
|
||||||
|
| Sanitized phone | `01012345678` | Backend | 정규화 전처리 |
|
||||||
|
| Login ID | `+821012345678` | Backend | Kratos credentials identifier 조회 |
|
||||||
|
| Identity ID | UUID | Kratos/Admin 조회 | 최종 subject 후보 |
|
||||||
|
|
||||||
|
표준성 판단:
|
||||||
|
|
||||||
|
- 전화번호 E.164 정규화는 표준적인 식별자 정규화에 해당한다.
|
||||||
|
- 그러나 전화번호 입력만으로 사용자를 인증하는 것은 표준 인증 기술이 아니다.
|
||||||
|
- 이 단계는 "식별" 단계이지 "인증" 단계가 아니다.
|
||||||
|
|
||||||
|
위험:
|
||||||
|
|
||||||
|
- 전화번호는 공유되거나 유출되기 쉽다.
|
||||||
|
- 번호 입력 성공/실패 응답이 다르면 사용자 존재 여부 열람(user enumeration)이 가능하다.
|
||||||
|
- 가입자 번호 재할당, 퇴사자 번호 회수, 가족/공용 단말 사용 같은 실제 운영 문제가 인증 실패로 이어질 수 있다.
|
||||||
|
|
||||||
|
권장 보완:
|
||||||
|
|
||||||
|
- 존재 여부 응답을 일반화한다.
|
||||||
|
- 번호는 반드시 소유 증명 단계로 이어지게 한다.
|
||||||
|
- 사용자 원장에는 E.164, 국가 코드, 원본 표시값을 분리 저장한다.
|
||||||
|
|
||||||
|
### 4.2 사용자 존재 여부 조회
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. Backend가 정규화된 전화번호를 사용해 `UserExists` 또는 Kratos Admin identity 조회를 수행한다.
|
||||||
|
2. 사용자가 없으면 오류를 반환한다.
|
||||||
|
3. 사용자가 있으면 다음 세션 생성 단계로 진행한다.
|
||||||
|
|
||||||
|
생성 또는 조회되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 생성/조회 주체 | 용도 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| credentials identifier | Backend | Kratos identity 검색 조건 |
|
||||||
|
| identity id | Kratos | OIDC subject 또는 세션 대상 |
|
||||||
|
| user exists boolean | Backend | 로그인 진행 여부 |
|
||||||
|
|
||||||
|
표준성 판단:
|
||||||
|
|
||||||
|
- IdP 원장에서 identifier로 identity를 조회하는 행위 자체는 일반적이다.
|
||||||
|
- 다만 이 결과만으로 로그인 성공을 허용하면 인증 요소가 없다.
|
||||||
|
|
||||||
|
위험:
|
||||||
|
|
||||||
|
- 공격자가 전화번호 목록으로 등록 여부를 확인할 수 있다.
|
||||||
|
- 내부 API가 열려 있으면 대량 스캐닝이 가능하다.
|
||||||
|
|
||||||
|
권장 보완:
|
||||||
|
|
||||||
|
- rate limit, IP 제한, WAF 룰, device attestation 등을 추가한다.
|
||||||
|
- `404 User not registered` 대신 일반 메시지를 사용한다.
|
||||||
|
- 감사 로그에는 입력 원문이 아닌 마스킹된 번호와 해시를 남긴다.
|
||||||
|
|
||||||
|
### 4.3 Kratos Admin 세션 직접 발급 방식
|
||||||
|
|
||||||
|
이슈 #1248 초반 설계에서는 다음 흐름이 제안되었다.
|
||||||
|
|
||||||
|
1. `POST /api/v1/auth/phone-login` 요청을 받는다.
|
||||||
|
2. Backend가 전화번호로 Kratos identity ID를 찾는다.
|
||||||
|
3. Backend가 Kratos Admin API로 해당 identity에 대한 세션을 직접 생성한다.
|
||||||
|
4. 세션 토큰을 Userfront에 반환한다.
|
||||||
|
|
||||||
|
생성되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 생성 주체 | 저장/전달 위치 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Kratos session id | Kratos | Kratos DB, Backend 응답 처리 |
|
||||||
|
| Kratos session token | Kratos | Backend 응답, Userfront token store |
|
||||||
|
| authenticated_at | Kratos | 세션 메타데이터 |
|
||||||
|
| AAL | Backend 요청/Kratos | 세션 보증 수준 |
|
||||||
|
|
||||||
|
표준성 판단:
|
||||||
|
|
||||||
|
- 관리자 API를 통한 세션 생성 기능 자체가 제품 기능일 수는 있다.
|
||||||
|
- 그러나 사용자가 아무 인증 수단도 제시하지 않았는데 관리자 권한으로 세션을 만들어 주는 것은 사용자 인증 표준 흐름이 아니다.
|
||||||
|
- OAuth2/OIDC 관점에서도 인증 서버가 사용자의 본인성을 확인하지 않은 채 login challenge를 accept하면 인증 의미가 훼손된다.
|
||||||
|
|
||||||
|
현재 코드 상태:
|
||||||
|
|
||||||
|
- `issueKratosSession(ctx, identityID)` 함수가 존재한다.
|
||||||
|
- 현재 검색 기준 이 함수의 호출부는 확인되지 않는다.
|
||||||
|
- `OryProvider.IssueSession`은 `domain.ErrNotSupported`를 반환한다.
|
||||||
|
|
||||||
|
위험:
|
||||||
|
|
||||||
|
- Backend 권한 탈취 시 임의 사용자 세션 발급이 가능하다.
|
||||||
|
- 감사 로그에는 "정상 로그인"처럼 남을 수 있으나 실제 사용자 행위가 아니다.
|
||||||
|
- AAL1로 표시되더라도 실질적 authenticator가 없으므로 보증 수준 해석이 왜곡된다.
|
||||||
|
|
||||||
|
권장 보완:
|
||||||
|
|
||||||
|
- 운영 로그인 경로로 사용하지 않는다.
|
||||||
|
- 필요한 경우 break-glass 관리자 지원 기능으로만 제한하고, 별도 승인/티켓/감사/만료 정책을 둔다.
|
||||||
|
- 관리자 세션 발급 기능은 feature flag로 기본 비활성화한다.
|
||||||
|
|
||||||
|
### 4.4 Courier 인터셉트 및 Code Claim 방식
|
||||||
|
|
||||||
|
이슈 #1248 후반 코멘트 및 #1245, #1247에서는 실제 구현에 가까운 방식으로 Courier 인터셉트가 설명되어 있다.
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. Backend가 전화번호를 기반으로 Kratos code login flow를 시작한다.
|
||||||
|
2. Kratos가 SMS 또는 email courier 발송을 시도한다.
|
||||||
|
3. Kratos Courier Webhook이 Backend의 relay endpoint를 호출한다.
|
||||||
|
4. Backend가 `template_data.login_code`를 추출한다.
|
||||||
|
5. Backend가 추출한 code를 즉시 Kratos `self-service/login?flow=...`에 제출한다.
|
||||||
|
6. Kratos가 session token을 발급한다.
|
||||||
|
7. Backend가 Redis pending session을 success로 갱신한다.
|
||||||
|
8. Poll 또는 후속 처리에서 Userfront/RP가 로그인 완료를 확인한다.
|
||||||
|
|
||||||
|
생성되는 데이터:
|
||||||
|
|
||||||
|
| 단계 | 데이터 | 생성 주체 | 저장 위치 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| flow init | flow id | Kratos | Redis `login_code_flow:{loginID}` |
|
||||||
|
| pending 생성 | pendingRef | Backend | Redis `enchanted_session:{pendingRef}` |
|
||||||
|
| pending 매핑 | loginID -> pendingRef | Backend | Redis `login_code_pending:{loginID}` |
|
||||||
|
| courier | login_code | Kratos | Webhook payload |
|
||||||
|
| code 저장 | normalized login code | Backend | Redis `login_code_value:{pendingRef}` |
|
||||||
|
| code verify | session token, session id | Kratos | Redis session payload 또는 응답 |
|
||||||
|
| audit | login event | Backend | Audit DB |
|
||||||
|
|
||||||
|
표준성 판단:
|
||||||
|
|
||||||
|
- Kratos code login flow 자체는 IdP의 정상 self-service login flow이다.
|
||||||
|
- Courier Webhook을 통해 메시지를 발송 대행하는 구조도 제품 통합 방식으로 볼 수 있다.
|
||||||
|
- 그러나 사용자가 수신한 OTP/SMS를 직접 확인하지 않고, 서버가 코드를 가로채 자동 제출하는 것은 OTP의 본래 보안 속성인 "사용자 소유 채널 확인"을 제거한다.
|
||||||
|
- 따라서 전체 로그인 방식은 표준 passwordless/SMS OTP 인증으로 보기 어렵다. 표준 기술을 사용해 비표준 인증 우회를 구성한 형태에 가깝다.
|
||||||
|
|
||||||
|
위험:
|
||||||
|
|
||||||
|
- 전화번호만 알면 세션 발급까지 진행될 수 있다.
|
||||||
|
- SMS 비용을 줄이는 대신 소유 증명이 사라진다.
|
||||||
|
- Courier relay가 내부 인증 없이 외부에서 호출 가능하면 코드 주입 또는 흐름 교란이 가능하다.
|
||||||
|
- Redis pending key 탈취 시 승인 흐름이 오염될 수 있다.
|
||||||
|
|
||||||
|
권장 보완:
|
||||||
|
|
||||||
|
- Courier relay endpoint는 내부 네트워크 또는 mTLS, shared secret, HMAC signature로 보호한다.
|
||||||
|
- code는 저장하지 않고 즉시 처리하되, 저장이 필요하면 TTL을 매우 짧게 유지한다.
|
||||||
|
- 자동 제출은 운영 사용자 로그인에 사용하지 않고, 테스트/개발 dry-run에 한정한다.
|
||||||
|
- 실서비스에서는 사용자에게 SMS OTP를 전달하고 사용자가 입력하도록 한다.
|
||||||
|
|
||||||
|
### 4.5 Headless client assertion + 폰번호 로그인
|
||||||
|
|
||||||
|
이슈 #1241은 RP가 `client_assertion`을 제출하고, 사용자는 전화번호만 전달하는 headless API를 제안한다.
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. RP가 `client_id`, `client_assertion`, `phoneNumber`, `login_challenge`를 Backend에 제출한다.
|
||||||
|
2. Backend가 Hydra login request를 조회한다.
|
||||||
|
3. Backend가 RP의 JWT client assertion을 JWKS로 검증한다.
|
||||||
|
4. Backend가 전화번호로 사용자를 조회한다.
|
||||||
|
5. Backend가 세션 발급 또는 code login flow를 진행한다.
|
||||||
|
6. Backend가 Hydra `AcceptLoginRequest`를 호출한다.
|
||||||
|
7. RP는 `redirectTo` 또는 OIDC authorization code 흐름을 이어간다.
|
||||||
|
|
||||||
|
생성되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 생성 주체 | 표준 기술 여부 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| client assertion JWT | RP | OAuth2 JWT client authentication 계열 |
|
||||||
|
| JWKS key | RP | JOSE/JWK 표준 |
|
||||||
|
| login challenge | Hydra | Ory Hydra/OIDC 로그인 플로우 |
|
||||||
|
| Kratos subject | Kratos | IdP subject |
|
||||||
|
| redirectTo | Hydra | OAuth2/OIDC authorization redirect |
|
||||||
|
|
||||||
|
표준성 판단:
|
||||||
|
|
||||||
|
- `client_assertion`과 JWKS 검증은 OAuth2/OIDC에서 사용하는 표준적인 클라이언트 인증 방식에 가깝다.
|
||||||
|
- Hydra login challenge, authorization code, PKCE는 표준 OIDC/OAuth2 기술이다.
|
||||||
|
- 그러나 이 검증은 RP 클라이언트가 신뢰 가능한지를 확인할 뿐, 최종 사용자가 전화번호 소유자임을 증명하지 않는다.
|
||||||
|
- 따라서 사용자 인증 단계는 여전히 비표준 또는 불충분한 상태다.
|
||||||
|
|
||||||
|
위험:
|
||||||
|
|
||||||
|
- 신뢰된 RP가 잘못 구현되거나 침해되면 임의 전화번호 로그인 시도가 가능하다.
|
||||||
|
- `client_assertion`을 사용자 인증으로 오해할 수 있다.
|
||||||
|
- RP별 정책 편차가 커지면 SSO의 인증 보증 수준이 불명확해진다.
|
||||||
|
|
||||||
|
권장 보완:
|
||||||
|
|
||||||
|
- headless API는 confidential client만 허용한다.
|
||||||
|
- RP별로 `headless_phone_login_enabled` 같은 명시적 allowlist를 둔다.
|
||||||
|
- 사용자 인증은 별도로 SMS OTP, WebAuthn, 앱 푸시 승인, 사전 등록 단말 증명 중 하나를 요구한다.
|
||||||
|
- `acr` 또는 `amr` claim에 실제 인증 방식을 명확히 기록한다.
|
||||||
|
|
||||||
|
### 4.6 Hydra AcceptLoginRequest 및 OIDC 연동
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. Backend가 Hydra login challenge를 조회한다.
|
||||||
|
2. 인증 완료로 판단한 subject를 결정한다.
|
||||||
|
3. Backend가 `AcceptLoginRequest(login_challenge, subject)`를 호출한다.
|
||||||
|
4. Hydra가 RP로 돌아갈 `redirectTo`를 반환한다.
|
||||||
|
5. RP는 authorization code 또는 token 교환을 수행한다.
|
||||||
|
|
||||||
|
생성되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 생성 주체 | 용도 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| subject | Backend/Kratos | OIDC user identifier |
|
||||||
|
| redirectTo | Hydra | RP redirect |
|
||||||
|
| authorization code | Hydra | token endpoint 교환 |
|
||||||
|
| id/access/refresh token | Hydra | RP 세션 수립 |
|
||||||
|
|
||||||
|
표준성 판단:
|
||||||
|
|
||||||
|
- Hydra의 OAuth2/OIDC 처리 자체는 표준 흐름이다.
|
||||||
|
- PKCE가 적용된 authorization code flow는 표준 보안 권고에 부합한다.
|
||||||
|
- 단, Hydra가 accept하는 전제인 "사용자 인증 완료"가 비표준 방식이면 전체 로그인 보증 수준은 낮아진다.
|
||||||
|
|
||||||
|
권장 보완:
|
||||||
|
|
||||||
|
- 비표준 인증으로 accept한 경우 `amr=["phone_identifier_only"]`처럼 별도 표시를 한다.
|
||||||
|
- 민감 RP에는 해당 `amr`을 허용하지 않는다.
|
||||||
|
- RP별 required ACR 정책을 둔다.
|
||||||
|
|
||||||
|
### 4.7 감사 로그 및 로그아웃 바인딩
|
||||||
|
|
||||||
|
프로세스:
|
||||||
|
|
||||||
|
1. 로그인 성공 후 Backend가 audit log를 기록한다.
|
||||||
|
2. OIDC accept 또는 consent granted 이벤트에 `session_id`, `client_id`, `user_id`를 남긴다.
|
||||||
|
3. 로그아웃 시 audit log에서 session-client binding을 복원한다.
|
||||||
|
4. Hydra refresh revoke 및 backchannel logout을 수행한다.
|
||||||
|
|
||||||
|
생성되는 데이터:
|
||||||
|
|
||||||
|
| 데이터 | 생성 주체 | 용도 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| audit event | Backend | 로그인/승인 이력 |
|
||||||
|
| session_id | Kratos/Backend | 세션 식별 |
|
||||||
|
| client_id | Hydra/RP | RP 식별 |
|
||||||
|
| consent event | Backend | RP 동의/연동 추적 |
|
||||||
|
|
||||||
|
표준성 판단:
|
||||||
|
|
||||||
|
- Backchannel Logout, refresh token revoke는 OIDC/OAuth2 생태계의 표준적 세션 관리 기술이다.
|
||||||
|
- 감사 로그 기반 바인딩 복원은 내부 구현 전략이며 표준 자체는 아니다.
|
||||||
|
|
||||||
|
위험:
|
||||||
|
|
||||||
|
- 감사 로그 누락 또는 비동기 지연 시 로그아웃 전파 누락 가능성이 있다.
|
||||||
|
- 비표준 로그인으로 생성된 세션과 표준 로그인 세션이 같은 수준으로 취급될 수 있다.
|
||||||
|
|
||||||
|
권장 보완:
|
||||||
|
|
||||||
|
- 세션-클라이언트 바인딩은 감사 로그 외에도 명시적 저장소를 둘지 검토한다.
|
||||||
|
- 비표준 로그인 세션에는 별도 `login_method`, `amr`, `risk_level`을 남긴다.
|
||||||
|
|
||||||
|
## 5. 표준 기술과 비표준 요소 매핑
|
||||||
|
|
||||||
|
| 구성 요소 | 적용 기술 | 표준/비표준 판단 | 비고 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 전화번호 E.164 정규화 | E.164 형식 | 표준 | 식별자 정규화 |
|
||||||
|
| Kratos self-service code login | Ory Kratos code login | 제품 표준 기능 | 사용자가 코드를 확인해야 인증 의미가 있음 |
|
||||||
|
| SMS OTP | One-time code over SMS | 일반적 MFA/passwordless 구현 | SIM swap, SMS 탈취 위험은 별도 존재 |
|
||||||
|
| Courier Webhook | Ory Courier 통합 | 제품 통합 기능 | 발송 대행은 가능 |
|
||||||
|
| Courier code 자동 추출/제출 | 내부 우회 | 비표준 | 사용자 소유 채널 확인 제거 |
|
||||||
|
| Kratos Admin 세션 직접 생성 | Admin API | 운영 인증에는 비표준/고위험 | break-glass 외 비권장 |
|
||||||
|
| OAuth2 client assertion JWT | JWT client authentication | 표준 계열 | 클라이언트 인증이지 사용자 인증이 아님 |
|
||||||
|
| JWKS | JOSE/JWK | 표준 | 키 배포/검증 |
|
||||||
|
| Hydra login challenge accept | OIDC/OAuth2 | 표준 | 인증 전제가 중요 |
|
||||||
|
| Authorization Code + PKCE | OAuth2/OIDC | 표준 | public client 권장 흐름 |
|
||||||
|
| Backchannel Logout | OIDC Back-Channel Logout | 표준 | RP 지원 필요 |
|
||||||
|
| Refresh token revoke | OAuth2 Token Revocation | 표준 | RP/AS 구현 정책 필요 |
|
||||||
|
|
||||||
|
## 6. 대체 기술 및 보완 제안
|
||||||
|
|
||||||
|
### 6.1 권장안 A: SMS OTP 기반 폰 로그인
|
||||||
|
|
||||||
|
사용자 UX:
|
||||||
|
|
||||||
|
1. 전화번호 입력
|
||||||
|
2. SMS OTP 수신
|
||||||
|
3. OTP 입력
|
||||||
|
4. Kratos code login verify
|
||||||
|
5. Hydra accept
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 전화번호 소유 확인이 가능하다.
|
||||||
|
- 기존 Kratos code login 구조를 가장 자연스럽게 사용한다.
|
||||||
|
- 사용자 인증 방식이 명확하다.
|
||||||
|
|
||||||
|
보완:
|
||||||
|
|
||||||
|
- OTP TTL 3~5분
|
||||||
|
- 재전송 제한
|
||||||
|
- 번호별/IP별 rate limit
|
||||||
|
- 실패 횟수 제한
|
||||||
|
- SIM swap 위험 안내 및 고위험 RP 추가 인증
|
||||||
|
|
||||||
|
### 6.2 권장안 B: WebAuthn/FIDO2 패스키
|
||||||
|
|
||||||
|
사용자 UX:
|
||||||
|
|
||||||
|
1. 전화번호 또는 계정 식별자 입력
|
||||||
|
2. 브라우저/OS 패스키 인증
|
||||||
|
3. Kratos 세션 발급
|
||||||
|
4. Hydra accept
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 피싱 저항성이 높다.
|
||||||
|
- SMS보다 강한 사용자 인증이다.
|
||||||
|
- 반복 로그인 UX가 빠르다.
|
||||||
|
|
||||||
|
보완:
|
||||||
|
|
||||||
|
- 초기 등록 절차 필요
|
||||||
|
- 분실/기기 교체 복구 정책 필요
|
||||||
|
|
||||||
|
### 6.3 권장안 C: 사내 전용 단말/키오스크 제한 모드
|
||||||
|
|
||||||
|
휴대폰 번호 단독 UX를 반드시 유지해야 한다면 일반 로그인으로 보지 말고 "통제된 환경의 단말 로그인"으로 분리한다.
|
||||||
|
|
||||||
|
필수 조건:
|
||||||
|
|
||||||
|
- 전용 단말 인증서 또는 mTLS
|
||||||
|
- 고정 네트워크/IP allowlist
|
||||||
|
- 단말별 client credential
|
||||||
|
- 단말 등록/폐기 관리
|
||||||
|
- 사용자별 허용 RP 제한
|
||||||
|
- 짧은 세션 TTL
|
||||||
|
- 민감 기능 재인증
|
||||||
|
|
||||||
|
판단:
|
||||||
|
|
||||||
|
- 사용자는 전화번호로 식별하고, 실제 인증은 단말/네트워크/관리 정책이 대신 수행하는 구조다.
|
||||||
|
- 이 경우에도 `amr`에는 `trusted_device_phone_identifier`처럼 일반 로그인과 다른 방식을 명시해야 한다.
|
||||||
|
|
||||||
|
### 6.4 권장안 D: 모바일 앱 푸시 승인
|
||||||
|
|
||||||
|
사용자 UX:
|
||||||
|
|
||||||
|
1. PC/키오스크에서 전화번호 입력
|
||||||
|
2. 등록된 모바일 앱으로 푸시 승인
|
||||||
|
3. 사용자가 앱에서 승인
|
||||||
|
4. Backend가 Kratos/Hydra 흐름 완료
|
||||||
|
|
||||||
|
장점:
|
||||||
|
|
||||||
|
- 빠른 UX를 유지하면서 사용자 소유 기기 확인이 가능하다.
|
||||||
|
- QR 로그인과 유사한 승인 모델을 재사용할 수 있다.
|
||||||
|
|
||||||
|
보완:
|
||||||
|
|
||||||
|
- 앱 등록/기기 바인딩 필요
|
||||||
|
- 푸시 피로 공격 방지를 위한 rate limit와 number matching 필요
|
||||||
|
|
||||||
|
### 6.5 권장안 E: 공통 승인 화면 적용
|
||||||
|
|
||||||
|
현재 작업 트리에 있는 `approval/info`, `approval/reject`, `LoginApprovalScreen` 계열 변경은 QR/링크 같은 비표준 로그인에 승인 전 정보를 보여주는 보완책이다.
|
||||||
|
|
||||||
|
권장 적용:
|
||||||
|
|
||||||
|
- QR, 링크, headless link 모두 공통 승인 화면 사용
|
||||||
|
- 승인 전에 요청 서비스, 기기, IP, 시간 표시
|
||||||
|
- `내 요청이 아닙니다` 선택 시 pending 상태를 `rejected` 또는 `blocked`로 변경
|
||||||
|
- 승인 거절 이벤트를 감사 로그로 남김
|
||||||
|
|
||||||
|
주의:
|
||||||
|
|
||||||
|
- 승인 화면은 "보완책"이지 전화번호 단독 로그인의 인증 부재를 완전히 해결하지 않는다.
|
||||||
|
- 사용자가 직접 승인하는 별도 소유 기기나 세션이 있을 때 의미가 있다.
|
||||||
|
|
||||||
|
## 7. 운영 정책 제안
|
||||||
|
|
||||||
|
### 7.1 기본 정책
|
||||||
|
|
||||||
|
- 휴대폰 번호만으로 세션을 발급하는 API는 운영 기본 경로로 노출하지 않는다.
|
||||||
|
- `POST /api/v1/auth/phone-login` 형태의 단일 요청 즉시 로그인은 금지한다.
|
||||||
|
- Courier code 자동 제출은 개발/테스트 dry-run 또는 제한된 내부 시나리오로만 허용한다.
|
||||||
|
- 모든 비표준 로그인에는 `login_method`, `amr`, `risk_level`, `client_id`, `device_id`를 남긴다.
|
||||||
|
|
||||||
|
### 7.2 RP별 정책
|
||||||
|
|
||||||
|
- RP별로 허용 가능한 인증 강도를 설정한다.
|
||||||
|
- 민감 RP는 SMS OTP 이상 또는 WebAuthn을 요구한다.
|
||||||
|
- headless 로그인 허용 RP는 별도 allowlist와 보안 심사를 거친다.
|
||||||
|
|
||||||
|
### 7.3 감사 및 탐지
|
||||||
|
|
||||||
|
- 전화번호 기반 로그인 시도는 성공/실패 모두 감사 로그에 남긴다.
|
||||||
|
- 동일 IP의 다수 번호 시도, 동일 번호의 반복 실패, 짧은 시간 내 다수 RP 로그인은 탐지 대상으로 둔다.
|
||||||
|
- 비표준 로그인으로 발급된 세션은 대시보드와 관리자 화면에서 구분 표시한다.
|
||||||
|
|
||||||
|
## 8. 권장 구현 방향
|
||||||
|
|
||||||
|
1. 폰번호 단독 즉시 로그인은 구현하더라도 기본 비활성 feature flag로 둔다.
|
||||||
|
2. 운영 사용자 로그인에는 SMS OTP 또는 WebAuthn을 붙인다.
|
||||||
|
3. 키오스크/전용 단말 요구사항이라면 별도 라우트와 별도 보증 수준으로 분리한다.
|
||||||
|
4. Hydra accept 시 `amr`/`acr`를 명확히 기록하고 RP 정책과 연결한다.
|
||||||
|
5. Courier relay는 내부망 또는 서명 검증으로 보호한다.
|
||||||
|
6. 공통 승인 화면을 QR/링크/headless link에 적용해 사용자가 요청 정보를 확인하게 한다.
|
||||||
|
7. 표준 로그인과 비표준 로그인의 감사 로그, 세션 TTL, RP 접근 권한을 분리한다.
|
||||||
|
|
||||||
|
## 9. 최종 판단
|
||||||
|
|
||||||
|
휴대폰 번호 단독 로그인은 "간편한 식별 UX"로는 사용할 수 있지만, 그 자체를 사용자 인증으로 간주하면 안 된다.
|
||||||
|
|
||||||
|
Baron SSO의 표준 인증 경로로 채택하려면 다음 중 하나가 반드시 추가되어야 한다.
|
||||||
|
|
||||||
|
- SMS OTP 또는 음성 OTP
|
||||||
|
- WebAuthn/FIDO2 패스키
|
||||||
|
- 등록된 모바일 앱 푸시 승인
|
||||||
|
- 사내 전용 단말 인증 및 네트워크 제한
|
||||||
|
- 관리자 승인 또는 업무 시스템 내 2차 승인
|
||||||
|
|
||||||
|
이 중 아무 것도 없는 `phoneNumber -> session` 구조는 표준 인증이 아니라 인증 우회에 가깝다. 따라서 운영 적용 시에는 "비표준 저보증 로그인"으로 명시하고, 접근 가능한 RP와 기능 범위를 제한해야 한다.
|
||||||
Reference in New Issue
Block a user