Skip to content

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.devuser
start.1pass.devuser (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/approveCli::AuthorizationsController#require_developer_or_admin (2026-05-19 추가, codex BLOCKER 반영)
/account/*일반 user 가 본인 진입 — 게이트 없음 (의도된 동작)

신규 surface 추가 시 의무 체크리스트

start.1pass.dev 에 새 라우트/컨트롤러를 올릴 때 반드시 확인:

  1. 민감 작업인가? (RP 등록·시크릿 발급·관리자 권한 등) → 컨트롤러에 require_developer_or_admin (또는 더 엄격한 게이트) 추가.
  2. PII 가 노출되나? → 본인만 접근하도록 Current.user 매칭 검사.
  3. 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.rb
  • spec/requests/cli/authorizations_spec.rb#"rejects regular user"

관련 코드

  • app/policies/handoff_policy.rb
  • app/controllers/session_handoffs_controller.rb
  • app/controllers/api/v1/me/cross_host_handoffs_controller.rb
  • app/models/session_handoff_token.rb
  • config/initializers/cross_host_handoff.rb

MIT License · Identity가 제품의 신뢰를 만듭니다.