← 기술 지식

기술 지식 / 보안 / 인증·계정

외부 신원과 계정 연결

이메일 일치와 소유권 증명을 나누고 issuer·subject 유일성, 시작 세션 바인딩, 연결 이후 세션 재평가를 설명합니다.

보안 · 원리·구현·운영
기술 지식 트리

계정 연결은 새 로그인 수단을 추가하는 보안 변경이지, callback에 들어온 이메일을 기존 계정에 붙이는 문자열 병합이 아닙니다. 기존 계정 소유권, 새 외부 신원의 검증, 연결 의도를 같은 일회성 시도와 저장 거래에 묶고, 이후 세션의 권한을 다시 평가해야 합니다.

이메일 일치 기반 계정 통합의 위험

우리 서비스의 계정 A가 이메일 하나를 가지고 있고, 새 소셜 로그인 응답에도 같은 이메일이 들어 있다고 합시다. 이 두 문자열의 일치는 공급자가 같은 사람을 같은 신뢰 수준으로 확인했다는 증거가 아닙니다. 이메일은 변경·재할당될 수 있고 공급자마다 검증 의미도 다릅니다.

외부 로그인 신원에서 issuer는 어떤 공급자가 발급했는지를 나타내는 식별자이고, subject는 그 공급자가 정한 범위에서 사용자를 가리키는 식별자입니다. 따라서 서버에는 검증한 issuer와 subject의 쌍을 저장하고, subject만 키로 쓰지 않아야 서로 다른 공급자의 같은 문자열이 충돌하지 않습니다.

공급자 URL을 임의로 정규화하거나 이메일을 소문자로 바꾸는 규칙은 프로토콜이 정의한 신원 비교를 대신할 수 없습니다. 공급자에 따라 subject가 client 또는 sector 범위별로 달라질 수 있으므로 client를 옮길 때도 이메일 자동 병합이 아니라 별도 신원 이전 절차를 거쳐야 합니다.

계정 연결을 구성하는 세 가지 증거

증거 확인할 질문 충분하지 않은 대체값
기존 계정 접근 현재 계정 A의 사용자가 민감 변경을 승인했는가 오래된 세션이 존재함
새 로그인 수단 소유 공급자 P의 신원 S를 새로 증명했는가 같은 이메일을 주장함
연결 의도 그 증명을 A의 로그인 수단 추가에 쓰기로 했는가 일반 로그인 callback 도착

계정 연결은 일반 로그인과 다른 작업이므로 별도의 purpose로 기록합니다. 사용자가 A에서 연결을 시작하면 서버는 A, 그때의 현재 세션, 재인증 시각, 선택한 공급자, 만료 시각, 한 번만 사용할 state를 하나의 시도에 묶습니다. callback이 돌아왔을 때 브라우저의 현재 계정이 B라면 callback 자체만 보고 B에 연결하지 않고, 시작 시 저장한 A·세션과 맞는지 먼저 비교합니다. 값이 맞지 않으면 연결을 거절하고 사용자가 올바른 계정에서 다시 시작하게 해야 합니다.

두 신원과 명시적 의도를 한 연결에 묶습니다계정 A 재인증: 시작 세션 고정. 공급자 P의 subject S: OIDC 검증 통과. 연결 시도 T 확인: A·세션·P·용도·기한. 유일한 신원 연결 저장: (P,S) → A기존 소유 증거새 수단 증거의도와 충돌 검사계정 A 재인증시작 세션 고정공급자 P의 subject SOIDC 검증 통과연결 시도 T 확인A·세션·P·용도·기한유일한 신원 연결 저장(P,S) → A
두 신원과 명시적 의도를 한 연결에 묶습니다 화살표는 연결 허용에 필요한 입력입니다. 이메일 일치 대신 검증된 계정 세션과 외부 신원, 일회성 연결 시도를 함께 확인합니다.
그림을 글로 읽기
  • 계정 A 재인증: 시작 세션 고정
  • 공급자 P의 subject S: OIDC 검증 통과
  • 연결 시도 T 확인: A·세션·P·용도·기한
  • 유일한 신원 연결 저장: (P,S) → A
  1. 계정 A 재인증 → 연결 시도 T 확인: 기존 소유 증거
  2. 공급자 P의 subject S → 연결 시도 T 확인: 새 수단 증거
  3. 연결 시도 T 확인 → 유일한 신원 연결 저장: 의도와 충돌 검사

세 증거는 각각 다른 공격을 막습니다. 기존 계정 접근은 A를 바꿀 권한을, 새 로그인 수단 소유는 P·S를 통제한다는 사실을, 연결 의도는 일반 callback이 임의의 계정 변경으로 전환되지 않음을 증명합니다. callback의 브라우저 session과 시작 시 저장한 session이 다르면, 외부 인증이 성공했더라도 link가 아니라 중단 후 재시작으로 처리해야 합니다.

유일성 제약과 저장 경계

두 계정이 동시에 같은 외부 신원을 연결하려고 할 수 있습니다. 둘 다 먼저 조회했을 때 연결이 없다는 결과를 얻더라도 최종 저장은 하나만 성공해야 합니다. 신원 테이블의 (issuer, subject)에 unique 제약을 두고 연결 시도 소비·연결 생성·계정 보안 버전 변경을 가능한 한 같은 거래로 처리합니다.

begin transaction
  lock and validate attempt T:
      purpose=link, account=A, session=original, not expired, not consumed
  validate current account security state and required reauthentication
  insert external_identity(P, S, A) with unique(P, S)
  mark T consumed
  increment A.security_version
  append security notification to outbox
commit

이미 A에 연결되어 있으면 정책에 따라 기존 결과를 보여 줄 수 있습니다. 다른 계정 B에 연결되어 있으면 조용히 옮기거나 두 계정을 합치지 않습니다. 별도 계정 병합은 양쪽 소유권·데이터 충돌·복구·감사까지 필요한 더 큰 변경입니다. 사용자에게 B의 상세 정보를 노출하지 않고 충돌을 설명해야 합니다.

동시성 시험은 A와 B가 같은 (issuer, subject)를 거의 동시에 insert하고, callback 재전송으로 같은 attempt를 두 번 소비하는 경우를 포함합니다. 예상 결과는 DB unique 제약에서 한 연결만 성공하고 다른 요청은 안전한 충돌 결과를 받으며, 성공한 요청의 security_version·outbox가 거래 경계에 맞게 남는 것입니다. 애플리케이션 사전 조회만으로 unique 제약을 대체하지 않습니다.

연결 이후 기존 세션의 재평가

새 로그인 수단을 추가하면 앞으로 계정에 들어올 경로가 늘어납니다. 이미 탈취된 세션이 공격자의 로그인 수단을 추가하는 지속 접근 경로가 되지 않도록 최근의 적절한 재인증을 요구하고 기존 채널에 알립니다. 알림 발송 자체는 소유권 증명이 아닙니다.

수단을 추가하거나 삭제한 뒤에는 현재 세션 ID를 교체하거나 인증 강도와 계정의 보안 버전을 갱신하고, 다른 기기 세션을 유지할지 회수할지를 정책으로 정합니다. 보안 버전을 올려도 각 API가 그 값을 확인하지 않으면 기존 access token은 즉시 막히지 않으므로, 버전 갱신과 실제 차단 지점을 함께 확인해야 합니다. 약한 복구로 돌아온 세션이라면 새 강한 자격 등록을 곧바로 허용하지 않을 수 있습니다.

연결 해제에서는 마지막 로그인 수단인지 확인합니다. 비밀번호도 없고 다른 수단도 없는 계정에서 마지막 공급자를 제거하면 정상 사용자를 잠글 수 있습니다. 대체 수단 등록·검증을 먼저 완료하거나 명시적인 계정 종료 흐름으로 나눕니다. 이 검사도 동시에 두 수단을 삭제하는 경쟁을 막도록 같은 계정의 변경을 직렬화해야 합니다.

보안 버전 갱신을 실제 차단으로 연결하려면 각 인증 API가 token의 발급 버전과 현재 계정 버전을 비교하는지 확인해야 합니다. 수단 추가 직후 기존 세션·다른 기기 세션·복구 세션으로 민감 API를 호출하는 trace를 만들고, 정책에 맞는 세션만 통과하는지 봅니다. 알림 성공은 감사 증거일 뿐 인증 상태를 대신하지 않습니다.

순서 변경 시나리오 기반 확인

A에서 연결을 시작한 뒤 B로 로그인하고 callback을 보내는 경우, 두 계정이 같은 외부 신원을 동시에 연결하는 경우, 같은 callback 재전송을 시험합니다. 기대 결과는 시작 계정 바뀜 거절·유일한 연결·중복 소비 방지입니다.

기존 세션 탈취를 가정한 테스트에서는 재인증 없이 수단 추가가 되는지, 복구 직후 제한이 유지되는지, 알림·회수가 실패했을 때 기록과 재처리가 남는지 봅니다. 이메일 변경이나 공급자 client 이전에서 계정이 뜻하지 않게 합쳐지지 않는 것도 확인해야 합니다. 이 검증 목록은 실제 운영 계정이 아니라 테스트 계정으로 수행할 절차입니다.

테스트는 결과뿐 아니라 노출되지 않아야 할 정보도 assertion으로 둡니다. B가 callback을 보내도 A의 상세 정보나 외부 신원의 소유 계정 정보를 과도하게 보여 주지 않아야 하고, 같은 callback은 두 번째 소비에서 외부 효과를 만들지 않아야 합니다. 실제 운영 계정이 아닌 테스트 신원·격리 DB에서 수행한다는 기존 제한을 유지합니다.

후속·관련 개념

함께 읽을 문서

전체 기술 지식
선택 연습 · 관련 질문 4개

질문은 이해를 확인하는 보조 자료입니다. 학습 경로와 문서의 범위는 질문 수와 독립적입니다.