테마
Cross-Host Session Handoff — Threat Model
logi 는 host=role 분리 아키텍처. api.1pass.dev (end-user) 와 start.1pass.dev (developer/account) 가 cookie scope 를 공유하지 않는다 (embed.1pass.dev iframe 격리 때문에 cookie domain 을 .1pass.dev 로 못 넓힘). 같은 세션을 두 호스트에서 인증된 채로 쓰려면 일회용 핸드오프 토큰 (SessionHandoffToken) 으로 호스트별 cookie 를 발급해야 한다.
정책 한 줄
| 호스트 | 필요 role |
|---|---|
| api.1pass.dev | user |
| start.1pass.dev | user (2026-05-19 완화. 이전엔 developer) |
→ HandoffPolicy::REQUIRED_ROLES (app/policies/handoff_policy.rb).
왜 완화했는가
/account 가 일반 사용자 셀프서비스 진입점인데 start.1pass.dev 에 host-locked. 정책이 developer 만 허용하면 일반 사용자가 /session/new 직접 방문 후 로그인했을 때 /account 로 못 간다 → dead-end UX.
왜 안전한가 — 게이트는 각 controller 가 진다
정책 완화로 일반 user 가 start.1pass.dev cookie 를 만들 수 있다는 것은 "start 호스트에 도달할 수 있다" 만 의미. 민감 surface 는 자체 게이트 보유:
| surface | 게이트 |
|---|---|
/developer/* | Developer::BaseController#require_developer_or_admin |
/console/applications etc. | 동일 |
/cli/auth/start, /cli/auth/approve | Cli::AuthorizationsController#require_developer_or_admin (2026-05-19 추가, codex BLOCKER 반영) |
/account/* | 일반 user 가 본인 진입 — 게이트 없음 (의도된 동작) |
신규 surface 추가 시 의무 체크리스트
start.1pass.dev 에 새 라우트/컨트롤러를 올릴 때 반드시 확인:
- 민감 작업인가? (RP 등록·시크릿 발급·관리자 권한 등) → 컨트롤러에
require_developer_or_admin(또는 더 엄격한 게이트) 추가. - PII 가 노출되나? → 본인만 접근하도록
Current.user매칭 검사. - CLI scope 발급 같은 권한 상승 surface 인가? → developer 필수
RequireRecentOtp등 추가 보호.
게이트 누락 시 일반 user 가 start cookie 로 잠입 후 권한 상승 가능 → 권한 상승 취약점. codex review (2026-05-19) 가 이를 처음 잡아냄 (/cli/auth/start 가 게이트 없이 apps:manage 발급 가능했던 사례).
SessionHandoffToken 자체 invariant
TTL 30s— 토큰 발급 후 30초 안에 redeem 안 하면 만료.consumed_at칼럼으로 single-use 강제 (consume!가 atomic update).target_host필드로 mint 시점의 호스트 고정. 다른 호스트에서 redeem 하면consume!가 nil 반환 (codex post-merge fix). API 토큰을 start 에서 redeem 시도 → 거부.- mint 측에서
cookies.delete(:session_id)— 같은 세션에 대해 토큰과 cookie 가 동시에 살아있지 않음 (단일 auth artifact 정책).
위협 1 — 토큰 탈취 + 재사용
시나리오: 공격자가 handoff URL (/session/handoff?token=...) 을 가로챈다.
Mitigation:
- TTL 30초 — 가로채도 빠르게 만료.
- Single-use — 정상 사용자가 한 번 redeem 한 토큰은 죽음 (replay 차단).
- target_host 바인딩 — 다른 호스트로 우회 불가.
- HTTPS 강제 (production) — 평문 가로채기 차단.
위협 2 — Open redirect via return_to
시나리오: /session/handoff?token=...&return_to=https://attacker.com 으로 사용자를 임의 URL 로 보낼 수 있나?
Mitigation: CrossHostHandoff.safe_path? 가 path-only + per-host allowlist 만 통과. 외부 URL / 프로토콜 상대 / prefix-trap (/accountXYZ) 모두 거부 → fallback path 로 다이렉트.
회귀 방지 spec: spec/requests/session_handoffs_spec.rb#"failure modes".
위협 3 — 권한 상승 (start 호스트 진입 후 민감 surface 호출)
시나리오: 일반 user 가 handoff 로 start.1pass.dev cookie 획득 → /cli/auth/start?...&scope=apps:manage 호출.
Mitigation: 위 "게이트 체크리스트" 참조. 각 컨트롤러가 자체적으로 Current.user.developer? 검증. 정책 완화는 호스트 진입만 허용, power surface 호출은 별도 게이트.
회귀 방지 spec:
spec/policies/handoff_policy_spec.rbspec/requests/cli/authorizations_spec.rb#"rejects regular user"
관련 코드
app/policies/handoff_policy.rbapp/controllers/session_handoffs_controller.rbapp/controllers/api/v1/me/cross_host_handoffs_controller.rbapp/models/session_handoff_token.rbconfig/initializers/cross_host_handoff.rb