관리 화면이나 다른 기기에서 세션 상태가 바뀌면 현재 앱은 그 사실을 늦게 알 수 있었다. 실시간 연결이 살아 있으면 이벤트를 받았지만 연결이 잠깐 끊겼다면 다음 API 요청에서 인증 오류가 날 때까지 화면은 로그인 상태처럼 보였다. 두 경로의 사용자 안내도 달랐다.
세션 변경을 특정 팝업 이벤트가 아니라 앱 전체 인증 상태의 전이로 다뤄야 했다.
다중 인증 신호의 단일 상태 전이
실시간 세션 종료 이벤트, API의 인증 실패, 주기 세션 확인 결과가 같은 상태 변경 함수를 호출했다.
void InvalidateSession(SessionEndReason reason)
{
if (session.State == SessionState.Invalid) return;
session.MarkInvalid(reason);
requests.CancelAll();
navigator.ShowSessionEnded(reason);
}
중복 신호가 와도 요청 취소와 화면 이동은 한 번만 실행됐다.
세션 종료 처리를 특정 화면 객체에 두지 않고 앱 수명의 서비스에 두었다. 로그인 화면 위에 있거나 앱이 백그라운드인 경우에도 상태는 먼저 바뀌고, 화면이 활성화되면 안전한 안내를 한 번 표시했다. 이벤트 수신 시점과 UI 표시 시점을 분리했다.
내부 종료 사유와 사용자 안내의 분리
내부적으로는 권한 변경, 다른 로그인, 관리자 종료, 만료를 구분했다. 사용자에게 그대로 노출하면 불필요한 정보가 될 수 있어 제품 정책에 맞는 안내 문구로 변환했다.
로그에도 세션 종료 분류와 요청 ID만 남기고 토큰이나 계정 값은 기록하지 않았다.
세션 종료 전 중요 작업 결과 확인
세션이 바뀌는 순간 전송 중이던 요청은 서버에서 처리됐지만 응답을 못 받았을 수 있었다. 특히 중복 실행 영향이 있는 작업은 새로 로그인한 뒤 무작정 재시도하지 않고 조회 API나 작업 ID로 결과를 확인했다.
단순 조회는 새 세션에서 다시 불러오고, 입력 중이던 값은 민감도와 화면 정책에 따라 복원하거나 폐기했다.
테스트에서는 실시간 연결이 정상인 경우, 연결이 끊긴 사이 변경된 경우, API 요청 도중 변경된 경우를 나눴다. 같은 최종 상태에 도달하되 중복 팝업이나 자동 재연결이 없어야 했다.
전역 이벤트로서의 인증 변경
팝업 하나를 띄우는 것으로 끝내면 백그라운드 요청과 실시간 구독이 계속 움직일 수 있었다. 세션 상태를 먼저 바꾸고 모든 데이터 경로가 이를 따르게 해야 했다.
이 작업을 통해 보안 이벤트와 사용자 경험이 따로 있지 않다는 걸 배웠다. 권한을 즉시 막으면서도 사용자가 왜 다시 로그인해야 하고 진행 중 작업은 어떻게 됐는지 이해할 수 있어야 했다.
후속 확인 항목
이 문제를 고친 뒤 관련 공식 자료를 찾아 읽으면서, 당시 코드에서 우연히 맞았던 부분과 규칙으로 남겨야 할 부분을 나눠 봤다.
OWASP ASVS와 API Security 기준을 따라 다시 보면 화면에서 숨겼다는 사실은 권한 검사가 아니다. 계좌나 사용자 식별자를 클라이언트가 보내더라도 서버는 인증 주체가 그 객체에 접근할 수 있는지 매 요청에서 확인해야 한다.
클라이언트의 역할은 권한을 결정하는 것이 아니라 잘못된 조작을 줄이고, 거부 응답을 이전 데이터와 섞지 않으며, 민감한 정보를 로그나 저장소에 남기지 않는 데 가깝다.
다음에 외부 세션 변경 알림 정책 같은 문제를 보면 아래 순서부터 확인하려고 한다.
- 익명·인증 중·인증됨·만료 상태
- 재시도 가능한 오류와 사용자 입력 필요 오류
- 토큰 폐기와 화면 초기화 순서
- 동시에 도착한 만료 응답의 단일 처리
식별자 형식을 어렵게 만들거나 메뉴를 감추는 방법은 서버의 객체 수준 권한 검사를 대체하지 못한다.
공식 참고 자료
- OWASP ASVS 4.0.3 — 인증·세션·접근제어의 서버 측 검증 기준
- OWASP API Security Top 10 2023 — 객체 수준 권한과 인증·자원 소비 경계