From 680ed343feda0135e1c84d928a42dd27812ff1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AC=B8=ED=98=95=EC=84=9D?= Date: Mon, 22 Jun 2026 13:10:45 +0900 Subject: [PATCH] =?UTF-8?q?Add=20Baron=20SSO=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EB=B0=A9=EC=8B=9D=20=EB=B0=8F=20=EB=B9=84=ED=91=9C=EC=A4=80?= =?UTF-8?q?=20=EC=9D=B8=EC=A6=9D=20=ED=9D=90=EB=A6=84=20=EC=A0=95=EB=A6=AC?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...SO 로그인 방식 및 비표준 인증 흐름 정리.md | 525 ++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 Baron SSO 로그인 방식 및 비표준 인증 흐름 정리.md diff --git a/Baron SSO 로그인 방식 및 비표준 인증 흐름 정리.md b/Baron SSO 로그인 방식 및 비표준 인증 흐름 정리.md new file mode 100644 index 0000000..35e5d5c --- /dev/null +++ b/Baron SSO 로그인 방식 및 비표준 인증 흐름 정리.md @@ -0,0 +1,525 @@ +# Baron SSO 로그인 방식 및 비표준 인증 흐름 정리 + +작성일: 2026-06-22 + +## 1. 목적 + +본 문서는 Baron SSO의 로그인 방식을 표준 방식과 비표준 방식으로 구분하고, 특히 비표준 방식에서 데이터와 인증 상태가 어떻게 이동하는지 설명하기 위해 작성한다. + +팀장 요청사항은 다음 세 가지로 정리된다. + +- 표준 로그인 방식에 대한 이해 +- 비표준 로그인 방식에서 데이터와 인증 흐름에 대한 이해 및 설명 +- 사용자가 아닌 사람의 로그인 요청이 발견될 때 사용자에게 알리고, 차단 또는 비차단 요구를 받아 단계적으로 처리하는 방안 검토 + +## 2. 현재 로그인 방식 요약 + +Baron SSO는 Ory Kratos와 Ory Hydra를 기반으로 한다. + +- Kratos: 사용자 인증, 세션 발급, 비밀번호 및 코드 로그인 처리 +- Hydra: OIDC/OAuth2 인가 서버, RP 로그인 요청과 consent 처리 +- UserFront: 사용자 로그인 UI +- Backend: UserFront와 Ory 사이의 중계 및 Baron 커스텀 로그인 흐름 처리 + +현재 사용자 로그인 방식은 아래처럼 5개로 볼 수 있다. + +| 구분 | 방식 | 대표 엔드포인트 | 성격 | +| --- | --- | --- | --- | +| 표준 | ID/Password | `POST /api/v1/auth/password/login` | Kratos password login | +| 표준 | Login Code | `POST /api/v1/auth/login/code/verify` | Kratos code login | +| 비표준 | Enchanted Link | `POST /api/v1/auth/enchanted-link/init` -> `poll` | Baron pendingRef 기반 링크 승인 | +| 비표준 | Magic Link Verify | `POST /api/v1/auth/magic-link/verify` | Baron token 검증 및 verify-only | +| 비표준 | QR 로그인 | `POST /api/v1/auth/qr/init` -> `poll` -> `approve` | Baron 교차 기기 승인 | + +참고로 `POST /api/v1/auth/sms`, `POST /api/v1/auth/verify-sms`도 존재하지만, 위의 "표준 2개 + 비표준 3개" 분류에서는 별도 보조 또는 내부 토큰 기반 흐름으로 보는 것이 적절하다. + +## 3. 표준 방식 2개 + +### 3.1 ID/Password + +사용자가 로그인 ID와 비밀번호를 입력하면 UserFront가 Backend의 `POST /api/v1/auth/password/login`을 호출한다. + +Backend는 Kratos Public API에서 login flow를 만들고, Kratos에 다음 정보를 제출한다. + +- `identifier`: 로그인 ID +- `password`: 비밀번호 +- `method`: `password` + +Kratos 인증이 성공하면 세션 토큰과 Kratos 세션 쿠키가 발급된다. Baron Backend는 Kratos identity ID를 Baron의 subject로 해석하고, 사용자 상태가 로그인 가능한 상태인지 확인한다. + +OIDC 로그인 중이면 `login_challenge`도 함께 전달된다. 이 경우 Backend는 Hydra의 login request를 확인한 뒤 `AcceptLoginRequest`를 호출하고, Hydra가 내려준 `redirectTo`를 UserFront에 반환한다. UserFront는 이 URL로 이동하여 RP의 OIDC 흐름을 계속 진행한다. + +### 3.2 Login Code + +Login Code는 Kratos의 `method=code` 기반 로그인이다. 사용자는 이메일 또는 전화번호 등으로 전달받은 코드를 입력하고, Backend는 Kratos login flow에 해당 코드를 제출한다. + +핵심 데이터는 다음과 같다. + +- `loginId`: 코드가 발급된 사용자 식별자 +- `flowID`: Kratos login flow ID +- `code`: 사용자가 제출한 인증 코드 + +검증 성공 시 Kratos는 세션 토큰을 발급한다. Baron은 이 세션 토큰을 기준으로 UserFront 세션을 구성하거나, OIDC 흐름이 있으면 Hydra login accept로 이어간다. + +## 4. 비표준 방식 3개 + +비표준 방식은 Kratos/Hydra의 표준 프로토콜만으로 끝나지 않고, Baron Backend가 `pendingRef`, Redis 상태, 폴링, 교차 기기 승인 같은 추가 상태 머신을 운영한다. + +### 4.1 Enchanted Link + +Enchanted Link는 사용자가 로그인 ID를 입력하면 Backend가 로그인 대기 상태를 만들고, 링크 또는 코드 기반 승인을 기다리는 방식이다. + +대표 흐름은 다음과 같다. + +1. UserFront가 `POST /api/v1/auth/enchanted-link/init` 호출 +2. Backend가 사용자 존재 여부를 확인 +3. Backend가 Redis에 `pendingRef` 기준 대기 상태 저장 +4. 사용자에게 링크 또는 코드 전달 +5. UserFront는 `POST /api/v1/auth/enchanted-link/poll`로 상태 폴링 +6. 사용자가 링크를 열거나 코드를 검증하면 대기 상태가 승인됨 +7. 폴링 중인 원래 브라우저가 `sessionJwt`를 받아 로그인 완료 + +주요 데이터는 다음과 같다. + +| 데이터 | 의미 | +| --- | --- | +| `pendingRef` | 로그인 대기 요청의 임시 참조 키 | +| `loginId` | 로그인 대상 사용자 | +| `status` | `pending`, `approved`, `success`, `expired` 등 | +| `sessionJwt` | 승인 후 발급되는 세션 토큰 | + +이 방식의 특징은 인증 요청 기기와 승인 기기가 분리될 수 있다는 점이다. 따라서 "누가 요청했는지"와 "누가 승인했는지"를 반드시 분리해서 기록해야 한다. + +### 4.2 Magic Link Verify + +Magic Link Verify는 전달된 토큰을 검증하여 로그인 또는 승인만 수행하는 흐름이다. + +대표 흐름은 다음과 같다. + +1. Backend가 magic token을 발급하고 Redis에 저장 +2. 사용자가 링크를 클릭 +3. UserFront가 `POST /api/v1/auth/magic-link/verify` 호출 +4. Backend가 token을 조회하고 만료 여부 확인 +5. `verifyOnly` 여부에 따라 즉시 세션 발급 또는 원래 pending 요청 승인 + +주요 데이터는 다음과 같다. + +| 데이터 | 의미 | +| --- | --- | +| `token` | 링크에 포함된 일회성 검증 토큰 | +| `pendingRef` | 원래 로그인 요청이 있는 경우 연결되는 대기 키 | +| `verifyOnly` | 현재 창에서 로그인하지 않고 원래 요청만 승인할지 여부 | +| `approverSubject` | 승인을 수행한 사용자 identity | + +이 방식은 사용자가 링크를 누르는 위치에 따라 동작이 달라질 수 있다. 예를 들어 같은 브라우저에서 바로 로그인할 수도 있고, 다른 기기에서 원래 요청만 승인할 수도 있다. + +### 4.3 QR 로그인 + +QR 로그인은 요청 기기에서 QR을 표시하고, 이미 로그인된 승인 기기에서 QR을 스캔하여 요청 기기를 로그인시키는 흐름이다. + +대표 흐름은 다음과 같다. + +1. 요청 기기에서 UserFront가 `POST /api/v1/auth/qr/init` 호출 +2. Backend가 `pendingRef`와 QR payload 생성 +3. 요청 기기는 QR을 표시하고 `POST /api/v1/auth/qr/poll`로 상태 폴링 +4. 승인 기기는 QR을 스캔하고 `POST /api/v1/auth/qr/approve` 호출 +5. Backend가 승인 기기의 현재 세션을 확인 +6. 승인된 subject를 기준으로 pending 상태를 승인 처리 +7. 요청 기기의 poll 응답으로 `sessionJwt`가 내려가고 로그인 완료 + +주요 데이터는 다음과 같다. + +| 데이터 | 의미 | +| --- | --- | +| `pendingRef` | QR 로그인 요청 식별자 | +| `qrCode` | 요청 기기에 표시되는 QR 데이터 | +| `approverSessionID` | QR을 승인한 기기의 세션 ID | +| `approverSubject` | QR을 승인한 사용자 | +| `requestDevice` | QR을 띄운 기기의 IP, User-Agent, 대략적 위치 | + +QR 로그인은 편의성이 높지만, 타인이 QR을 띄우고 사용자가 실수로 승인하는 상황을 반드시 방어해야 한다. + +## 5. 비표준 방식의 공통 위험 지점 + +비표준 방식의 핵심 위험은 "요청 주체"와 "승인 주체"가 다를 수 있다는 데 있다. + +예를 들어 공격자가 자기 PC에서 로그인 링크 또는 QR 요청을 만들고, 실제 사용자에게 승인 행동을 유도할 수 있다. 이때 사용자가 아무 정보 없이 승인하면 공격자의 기기가 로그인될 수 있다. + +따라서 비표준 방식에는 다음 데이터가 필수로 남아야 한다. + +- 요청 생성 시각 +- 요청 만료 시각 +- 요청 IP +- 요청 User-Agent +- 요청 기기 유형 +- 요청 위치 추정값 +- 요청 RP 또는 client ID +- 승인 사용자 subject +- 승인 세션 ID +- 승인 기기 IP/User-Agent +- 승인 또는 거절 결과 + +## 6. 사용자가 아닌 사람의 로그인 요청 발견 시 처리 방안 + +### 6.1 기본 원칙 + +타인의 로그인 요청 가능성이 있는 경우, 시스템은 사용자가 판단할 수 있는 정보를 보여주고 명확한 선택지를 제공해야 한다. + +사용자에게 제공할 선택지는 최소 3개가 적절하다. + +| 사용자 선택 | 의미 | 처리 | +| --- | --- | --- | +| 내 요청입니다 | 사용자가 요청을 인지하고 승인 | 제한 조건 검증 후 승인 | +| 내 요청이 아닙니다 | 타인의 요청으로 판단 | 즉시 차단, pending 폐기, 보안 이벤트 기록 | +| 잘 모르겠습니다 | 확신 불가 | 기본 차단 또는 보류, 추가 확인 유도 | + +보안 관점의 기본값은 "비차단"이 아니라 "보류 또는 차단"이어야 한다. + +### 6.2 사용자 알림 방식 + +의심 요청이 생성되거나 승인 직전에 다음 정보를 사용자에게 보여준다. + +- 요청 서비스: 예) Baron SSO, DevFront, AdminFront, 특정 RP +- 요청 시간 +- 요청 기기: 브라우저, OS, 모바일/데스크톱 추정 +- 요청 위치: 국가/지역 수준의 추정값 +- 요청 IP 일부 마스킹 +- 승인 시 결과: "이 요청을 승인하면 해당 기기가 로그인됩니다." + +알림 채널은 상황에 따라 다르게 적용한다. + +| 상황 | 권장 알림 | +| --- | --- | +| QR 승인 화면 | 승인 화면에 요청 정보 직접 표시 | +| Magic Link 클릭 | 링크 검증 화면에 요청 정보 표시 | +| Enchanted Link 승인 | 승인 화면 또는 poll 연결 화면에 요청 정보 표시 | +| 고위험 요청 | 이메일/SMS/앱 알림 병행 | + +### 6.3 단계별 처리 흐름 + +#### 1단계: 요청 생성 + +Backend가 pending 요청을 만들 때 요청 메타데이터를 함께 저장한다. + +저장 예시는 다음과 같다. + +```json +{ + "pendingRef": "abc123", + "loginId": "user@example.com", + "requestIp": "203.0.113.xxx", + "requestUserAgent": "Chrome on Windows", + "requestDeviceType": "desktop", + "requestClientId": "userfront", + "requestedAt": "2026-06-22T10:00:00+09:00", + "expiresAt": "2026-06-22T10:05:00+09:00", + "status": "pending" +} +``` + +#### 2단계: 위험도 평가 + +요청 생성 시 또는 승인 직전에 risk score를 계산한다. + +위험도를 높이는 조건은 다음과 같다. + +- 최근 로그인 위치와 크게 다른 위치 +- 새 기기 또는 새 브라우저 +- 짧은 시간 내 반복 요청 +- 실패한 비밀번호 시도 직후 링크/QR 요청 +- 관리자 계정 또는 권한 높은 계정 +- 비활성/잠금/퇴사 상태 사용자 +- RP client가 inactive 또는 미승인 상태 + +#### 3단계: 사용자 확인 + +승인 화면에서 다음 문구를 제공한다. + +```text +새 로그인 요청이 감지되었습니다. + +요청 기기: Chrome on Windows +요청 위치: Seoul, KR +요청 시간: 2026-06-22 10:00 +요청 서비스: Baron SSO + +이 요청을 승인하면 해당 기기가 로그인됩니다. +본인이 요청한 로그인이 맞습니까? +``` + +버튼은 다음처럼 구성한다. + +- `내 요청입니다` +- `내 요청이 아닙니다` +- `잘 모르겠습니다` + +#### 4단계: 차단 선택 처리 + +사용자가 `내 요청이 아닙니다`를 선택하면 다음 순서로 처리한다. + +1. `pendingRef` 상태를 `blocked`로 변경 +2. 해당 pending 요청에서 세션 발급 금지 +3. poll 중인 요청 기기에는 `blocked_by_user` 응답 반환 +4. 감사 로그에 `login.request.blocked_by_user` 기록 +5. 사용자에게 비밀번호 변경 또는 전체 세션 로그아웃 안내 +6. 반복 발생 시 계정 보호 모드 전환 검토 + +요청 기기에는 구체적인 사용자 정보를 노출하지 않는다. 예시는 다음 정도가 적절하다. + +```json +{ + "error": "login_request_blocked", + "code": "blocked_by_user", + "message": "This login request was not approved." +} +``` + +#### 5단계: 비차단 선택 처리 + +사용자가 `내 요청입니다`를 선택하면 바로 승인하지 않고 최소 검증을 한 번 더 수행한다. + +검증 항목은 다음과 같다. + +- pending 요청이 만료되지 않았는지 +- 승인 사용자의 subject와 로그인 대상 subject가 같은지 +- 기존 브라우저 세션과 대상 subject가 충돌하지 않는지 +- RP client가 활성 상태인지 +- 사용자 상태가 로그인 가능 상태인지 + +검증 통과 후에만 세션 발급 또는 Hydra `AcceptLoginRequest`를 수행한다. + +#### 6단계: 보류 선택 처리 + +사용자가 `잘 모르겠습니다`를 선택하면 기본적으로 승인하지 않는다. + +처리 방식은 다음 중 하나가 적절하다. + +- 보안 우선: 즉시 `blocked_uncertain` 처리 +- UX 우선: `hold` 상태로 바꾸고 짧은 시간 뒤 만료 + +권장안은 보안 우선이다. 사용자가 모르는 요청은 사실상 승인하면 안 된다. + +## 7. 구현 검토 항목 + +### 7.1 데이터 모델 또는 Redis 상태 확장 + +비표준 로그인 pending 상태에 아래 필드를 공통으로 포함하는 것이 좋다. + +- `requestIp` +- `requestUserAgent` +- `requestDeviceType` +- `requestGeo` +- `requestClientId` +- `requestedAt` +- `expiresAt` +- `riskLevel` +- `riskReasons` +- `approvalDecision` +- `approvalSubject` +- `approvalSessionID` +- `approvalIp` +- `approvalUserAgent` + +### 7.2 API 응답 표준화 + +poll 계열 API는 상태를 명확히 분리해야 한다. + +| 상태 | 의미 | +| --- | --- | +| `authorization_pending` | 아직 승인 전 | +| `approved` | 승인됨, 세션 발급 가능 | +| `blocked_by_user` | 사용자가 차단 | +| `blocked_by_policy` | 정책상 차단 | +| `expired_token` | 만료 | +| `slow_down` | 폴링 속도 제한 | + +### 7.3 감사 로그 + +최소 이벤트는 다음과 같다. + +- `login.request.created` +- `login.request.viewed_by_user` +- `login.request.approved_by_user` +- `login.request.blocked_by_user` +- `login.request.blocked_by_policy` +- `login.request.expired` +- `login.session.issued` + +### 7.4 사용자 화면 + +QR, Magic Link, Enchanted Link 승인 화면은 공통 컴포넌트를 사용할 수 있다. + +공통 컴포넌트가 표시해야 할 정보는 다음과 같다. + +- 요청 서비스 +- 요청 기기 +- 요청 위치 +- 요청 시간 +- 승인 결과 설명 +- 차단/승인/보류 버튼 + +## 8. 결론 + +Baron SSO의 로그인 방식은 표준 2개와 비표준 3개로 정리할 수 있다. + +- 표준 방식은 Kratos의 password/code login을 기반으로 하므로 이해 지점은 Kratos flow와 세션 발급이다. +- 비표준 방식은 Baron이 pendingRef, Redis 상태, 폴링, 교차 기기 승인을 추가한 구조이므로 요청자와 승인자를 분리해서 이해해야 한다. +- 사용자가 아닌 사람의 로그인 요청을 막으려면 승인 직전에 요청 메타데이터를 사용자에게 보여주고, `내 요청입니다`, `내 요청이 아닙니다`, `잘 모르겠습니다` 선택을 받아 상태 머신으로 처리해야 한다. + +가장 중요한 정책은 다음 한 문장으로 요약된다. + +> 사용자가 명확히 본인 요청이라고 확인하지 않은 비표준 로그인 요청은 세션 발급으로 이어지면 안 된다. + +## 9. 최근 운영 이슈와의 연관성 + +최근 운영 저장소 이슈 중 로그인 방식 검토와 직접 연결되는 항목은 다음과 같다. + +| 이슈 | 연관도 | 검토 포인트 | +| --- | --- | --- | +| `#1249` Naver Works 버튼 기반 로그아웃 요청 | 매우 높음 | 로그인 성공 알림 후 사용자가 "내가 아닌 로그인"을 선택하면 refresh grant revoke로 세션을 끊는 구조이다. 본 문서의 차단 처리 흐름과 직접 연결된다. | +| `#1248` 휴대폰 번호 입력만으로 즉시 로그인 | 매우 높음 | 전화번호 하나만으로 세션을 만드는 설계이다. Kratos code flow를 활용하더라도 SMS 발송과 사용자 코드 입력을 생략하므로 고위험 비표준 흐름으로 분류해야 한다. | +| `#1247` Baron Backend와 Ory Kratos 인터랙션 분석 | 매우 높음 | Headless 방식, PKCE 방식, Courier 인터셉트 패턴을 설명한다. 표준/비표준 경계와 데이터 흐름 검토의 핵심 자료이다. | +| `#1246` 로그아웃 처리 방식 refresh revoke 전환 | 높음 | 타인 로그인 또는 의심 로그인 발견 후 세션을 종료하는 후속 조치와 연결된다. polling/Redis 기반보다 refresh revoke 기반이 최신 방향으로 보인다. | +| `#1245` QR/링크 로그인 분석 및 Headless 폰번호 적용 | 매우 높음 | 기존 비표준 방식인 QR/링크 흐름을 Headless 폰번호 로그인에 재사용하려는 설계이다. | +| `#1241` Headless 폰번호 전용 로그인 API | 매우 높음 | `client_assertion`, `phoneNumber`, `login_challenge`만으로 OIDC 로그인을 완료하는 API이다. 사용자 실재 인증 여부를 별도로 검토해야 한다. | +| `#1242` 세션 폐기 Redis 버퍼링 | 중간 | 세션 폐기 이벤트를 RP가 감지하게 하는 구조이나, `#1246`의 refresh revoke 방식과 정책 충돌 여부를 확인해야 한다. | +| `#1244` DevFront 로그인 후 언어 전환 이슈 | 낮음 | 로그인 이후 UX 문제이며 인증 흐름 자체와 직접 관련은 낮다. | +| `#1250` 인력정보 백업 및 스테이징 복구 | 중간 | Kratos identity 포함 여부와 credential/session reset policy가 로그인 데이터 관리와 간접 연결된다. | + +## 10. Headless 폰번호 로그인 추가 검토 + +`#1247`, `#1248`, `#1241`에서 제안된 Headless 폰번호 로그인은 다음 흐름을 가진다. + +### 10.1 단계별 데이터 이동 + +#### Step 1. 인가 및 전화번호 식별 + +RP가 Baron Backend에 다음 데이터를 전달한다. + +- `client_id` +- `client_assertion`: RP의 private key JWT 서명값 +- `phoneNumber`: 사용자가 입력한 전화번호 +- `login_challenge`: Hydra OIDC 로그인 요청 식별자 + +Backend는 먼저 RP가 폰번호 전용 로그인을 사용할 수 있는 기등록 클라이언트인지 검증한다. 이후 전화번호를 E.164 형식으로 정규화한다. + +예시: + +```text +010-1234-5678 -> +821012345678 +``` + +그 다음 정규화된 전화번호가 Baron/Kratos 원장에 존재하는지 확인한다. + +#### Step 2. 인터셉터 플래그 캐싱 + +Backend는 Redis에 인증 발송 차단 또는 내부 처리용 플래그를 저장한다. + +```text +prefixLoginCodePhonePending:+821012345678 -> interception_flag +TTL: 5m +``` + +이 플래그는 Kratos가 Courier를 통해 SMS 발송을 시도할 때, 실제 외부 SMS 발송으로 보내지 않고 Backend 내부 처리 흐름으로 분기시키기 위한 신호 역할을 한다. + +#### Step 3. 대리 인증 흐름 기동 + +Backend는 Kratos code login flow를 시작한다. + +```text +h.IdpProvider.InitiateLinkLogin("+821012345678", returnTo) +``` + +Kratos는 내부 login flow를 만들고 `flowID`를 반환한다. Backend는 이를 Redis에 저장한다. + +```text +prefixLoginCode:+821012345678 -> flowID +TTL: 5m +``` + +#### Step 4. SMS 인터셉트 및 원격 인증 자동 수립 + +Kratos Courier가 SMS 발송 웹훅을 Backend로 호출한다. + +```text +POST /api/v1/auth/kratos-courier-relay +``` + +Backend는 수신 번호에 `prefixLoginCodePhonePending` 플래그가 있는지 확인한다. 플래그가 있으면 외부 SMS 발송을 수행하지 않고, Courier payload 안의 `login_code`를 추출한다. + +이후 Backend는 Redis에서 `flowID`를 조회하고 Kratos에 code 검증을 제출한다. + +```text +VerifyLoginCode("+821012345678", flowID, login_code) +``` + +검증이 성공하면 Kratos가 공식 세션을 발급한다. Backend는 Kratos Courier에 200 OK를 반환하여 Courier 큐를 정상 종료시킨다. + +#### Step 5. OIDC 리다이렉트 합의 및 RP 응답 + +Backend는 확보한 subject/session 정보를 바탕으로 Hydra login request를 승인한다. + +```text +Hydra.AcceptLoginRequest(login_challenge, subject) +``` + +성공하면 RP에 다음 정보를 반환한다. + +- `redirectTo` +- `sessionId` +- `status` + +RP 또는 브라우저는 `redirectTo`를 따라가며 OIDC 흐름을 완료한다. + +### 10.2 표준 여부 판단 + +이 흐름은 Kratos의 code login과 Hydra OIDC를 사용한다는 점에서는 표준 컴포넌트를 재사용한다. 그러나 사용자 관점에서 핵심 인증 행위인 SMS 수신, 코드 입력, 명시적 승인 단계가 Backend 내부 자동 처리로 대체된다. + +따라서 이 방식은 "Ory 컴포넌트 기반"이라고 표현할 수는 있지만, "Ory 보안 표준 100% 준수" 또는 "취약점이 전혀 없음"이라고 표현하면 안 된다. Baron이 별도 신뢰 경계와 위험을 만든 비표준 고위험 로그인 방식으로 분류하는 것이 적절하다. + +### 10.3 주요 위험 + +이 설계의 위험은 다음과 같다. + +- 전화번호를 아는 것만으로 로그인 요청이 시작될 수 있다. +- RP의 `client_assertion`이 유출되거나 오용되면 대량 로그인 시도 통로가 될 수 있다. +- SMS가 실제 사용자에게 전달되지 않기 때문에 사용자가 로그인 시도를 인지하지 못할 수 있다. +- Courier payload의 `login_code`를 Backend가 내부 자동 검증하는 구조는 감사와 권한 경계가 매우 중요하다. +- 사용자의 명시적 승인 없이 Hydra `AcceptLoginRequest`까지 자동 처리되면 "누가 로그인 요청을 승인했는가"가 모호해질 수 있다. + +### 10.4 필수 통제 조건 + +Headless 폰번호 로그인을 유지하거나 구현하려면 최소한 다음 조건이 필요하다. + +- 허용 RP allowlist +- RP별 `headless_phone_login_enabled` 명시 설정 +- `private_key_jwt` 기반 `client_assertion` 검증 +- JWKS 캐시 및 키 회전 정책 +- 전화번호 enumeration 방지 응답 정책 +- IP, client, phoneNumber 기준 rate limit +- 고위험 계정 또는 관리자 계정 사용 금지 +- 로그인 성공 즉시 사용자 알림 +- 사용자의 "내가 아닌 로그인" 선택 시 refresh grant revoke +- 모든 단계의 감사 로그 기록 + +### 10.5 용어 정리 + +설계 문서나 보고서에는 "갈취"라는 표현 대신 다음 용어를 사용하는 것이 적절하다. + +| 피해야 할 표현 | 권장 표현 | +| --- | --- | +| 코드 갈취 | Courier payload에서 login_code 추출 | +| SMS 탈취 | Courier 발송 요청 인터셉트 | +| 강제 세션 생성 | Kratos code 검증을 통한 세션 발급 | +| 완전 차단 | 정책 기반 차단 또는 위험 완화 | +| 취약점이 전혀 없음 | 표준 컴포넌트를 재사용하나 별도 위험 통제 필요 | + +## 11. 팀장 보고용 핵심 결론 + +팀장 보고 시에는 다음처럼 정리하는 것이 안전하다. + +1. Baron SSO의 기존 로그인 방식은 표준 2개, 비표준 3개로 분류된다. +2. 최근 이슈의 Headless 폰번호 로그인은 기존 비표준 방식보다 더 강한 비표준 확장이다. +3. Kratos/Hydra를 사용한다고 해서 전체 흐름이 자동으로 표준 인증이 되는 것은 아니다. +4. 사용자가 직접 인증 코드 입력 또는 승인 행동을 하지 않는 흐름은 반드시 별도 보안 통제와 감사 로그가 필요하다. +5. "내가 아닌 로그인" 발견 시에는 Naver Works 알림, 사용자 차단 선택, refresh grant revoke, 감사 로그 기록까지 하나의 폐쇄 루프로 설계해야 한다.