다른 기기에서 같은 계정으로 로그인했을 때 기존 앱에 중복 로그인 알림이 나타나는 기능을 봤다. 알림이 늦게 뜨거나, 확인 버튼을 누른 뒤 로그인 화면으로 가지 않고 다시 연결되는 경우가 있었다. 처음에는 서버가 세션을 잘못 끊는 문제인지 클라이언트 화면 전환 문제인지 구분하기 어려웠다.
로그인을 허용하고 기존 세션을 만료시키는 판단은 서버에 있었고, 그 사실을 사용자에게 안전하게 보여주는 책임은 클라이언트에 있었다.
서버 이벤트와 화면 동작의 분리
클라이언트가 받은 이벤트를 바로 팝업 코드에 연결하지 않고 세션 상태로 변환했다.
void OnSessionRevoked(SessionEvent message)
{
session.MarkRevoked(message.Reason);
connection.StopReconnect();
navigator.ShowSessionEnded();
}
팝업이 닫혀 있거나 다른 화면 위에 있어도 세션 상태 자체는 만료된 채 유지됐다. 화면이 다시 열릴 때도 유효하지 않은 세션으로 요청하지 않았다.
재연결 시 만료 상태 보존
네트워크가 끊기면 자동 재연결하는 코드가 중복 로그인으로 연결이 닫힌 경우에도 실행됐다. 종료 원인을 구분하지 않으니 새 연결이 생겼다가 다시 거절되는 반복이 발생했다.
일시적 네트워크 종료 -> 세션 유지, 제한된 재연결
서버의 세션 만료 -> 재연결 중단, 로그인 화면
사용자 로그아웃 -> 로컬 정보 정리, 재연결 금지
연결 상태와 인증 상태를 하나의 bool로 다루지 않는 게 중요했다.
중복 이벤트의 단일 결과 보장
서버 알림과 다음 API의 인증 실패가 거의 동시에 도착할 수 있었다. 둘 다 세션 종료 화면을 열면 팝업이 두 개 생겼다. 세션 상태 전이를 멱등하게 만들어 이미 만료 처리됐다면 같은 결과를 다시 실행하지 않았다.
사용자에게는 내부 오류 코드 대신 다른 곳에서 로그인되어 세션이 종료됐다는 다음 행동을 설명했다. 계정이나 접속 위치 같은 정보는 필요한 범위 이상 표시하지 않았다.
책임 경계별 검증 증거
서버가 만료 이벤트를 보냈는지, 클라이언트가 받았는지, 재연결을 중단했는지, 화면이 전환됐는지를 따로 확인했다. 한 단계의 성공으로 전체가 정상이라고 결론 내리지 않았다.
중복 로그인 문제를 통해 서버 문제와 화면 문제 사이에 세션 상태라는 경계가 있다는 걸 배웠다. 이후 인증 오류를 보면 팝업 문구만 고치지 않고 누가 상태를 결정하고 다음 요청을 누가 막는지까지 따라가게 됐다.
후속 확인 항목
나중에 같은 증상을 다시 만나지 않으려고 문서에 적힌 기준으로 구현을 한 번 더 정리했다.
OWASP ASVS와 API Security 기준을 따라 다시 보면 화면에서 숨겼다는 사실은 권한 검사가 아니다. 계좌나 사용자 식별자를 클라이언트가 보내더라도 서버는 인증 주체가 그 객체에 접근할 수 있는지 매 요청에서 확인해야 한다.
클라이언트의 역할은 권한을 결정하는 것이 아니라 잘못된 조작을 줄이고, 거부 응답을 이전 데이터와 섞지 않으며, 민감한 정보를 로그나 저장소에 남기지 않는 데 가깝다.
다음에 중복 로그인 책임 경계 같은 문제를 보면 아래 순서부터 확인하려고 한다.
- 익명·인증 중·인증됨·만료 상태
- 재시도 가능한 오류와 사용자 입력 필요 오류
- 토큰 폐기와 화면 초기화 순서
- 동시에 도착한 만료 응답의 단일 처리
식별자 형식을 어렵게 만들거나 메뉴를 감추는 방법은 서버의 객체 수준 권한 검사를 대체하지 못한다.
공식 참고 자료
- OWASP ASVS 4.0.3 — 인증·세션·접근제어의 서버 측 검증 기준
- OWASP API Security Top 10 2023 — 객체 수준 권한과 인증·자원 소비 경계