사용자가 웹사이트에서 로그아웃하면 맞춤설정된 사용자 환경에서 완전히 벗어나야 한다는 필요성을 전달하는 것입니다. 따라서 사용자의 사고 모델을 최대한 준수하는 것이 중요합니다. 예를 들어 적절한 로그아웃 환경에서는 사용자가 로그아웃하기 전에 열었을 수 있는 탭도 고려해야 합니다.
우수한 로그아웃 환경의 핵심은 사용자 환경의 시각적 측면과 상태 측면에서 일관성을 유지하는 것입니다. 이 가이드에서는 주의해야 할 사항과 원활한 로그아웃 환경을 구현하는 방법에 관한 구체적인 조언을 제공합니다.
주요 고려사항
웹사이트에서 로그아웃 기능을 구현할 때는 원활하고 안전하며 직관적인 로그아웃 프로세스를 위해 다음 사항에 유의하세요.
- 명확하고 일관된 로그아웃 UX: 웹사이트 전체에서 쉽게 식별하고 액세스할 수 있는 명확하고 일관되게 표시되는 로그아웃 버튼 또는 링크를 제공합니다. 모호한 라벨을 사용하거나, 잘 보이지 않는 메뉴, 하위 페이지 또는 기타 직관적이지 않은 위치에 로그아웃 기능을 숨기지 마세요.
- 확인 메시지: 로그아웃 프로세스를 완료하기 전에 확인 메시지를 구현합니다. 이렇게 하면 사용자가 실수로 로그아웃하는 것을 방지할 수 있으며, 사용자가 강력한 비밀번호나 기타 인증 메커니즘으로 기기를 부지런히 잠그는 경우와 같이 로그아웃이 정말 필요한지 다시 생각해 볼 수 있습니다.
- 여러 탭 처리: 사용자가 동일한 웹사이트의 여러 페이지를 다른 탭에서 연 경우 한 탭에서 로그아웃하면 해당 웹사이트의 다른 모든 열린 탭도 업데이트되도록 합니다.
- 보안 방문 페이지로 리디렉션: 로그아웃에 성공하면 사용자가 더 이상 로그인되어 있지 않음을 명확하게 나타내는 보안 방문 페이지로 사용자를 리디렉션합니다. 개인 맞춤 정보가 포함된 페이지로 사용자를 리디렉션하지 마세요. 마찬가지로 다른 탭에도 로그인 상태가 더 이상 반영되지 않는지 확인합니다. 또한 공격자가 악용할 수 있는 오픈 리디렉션을 빌드하지 않도록 주의하세요.
- 세션 정리: 사용자가 로그아웃하면 사용자 세션과 연결된 민감한 사용자 세션 데이터, 쿠키 또는 임시 파일을 완전히 삭제합니다. 이렇게 하면 사용자 정보 또는 계정 활동에 대한 무단 액세스를 방지할 수 있으며, 브라우저가 다양한 캐시, 특히 뒤로/앞으로 캐시에서 민감한 정보가 포함된 페이지를 복원하는 것도 방지할 수 있습니다.
- 오류 처리 및 피드백: 사용자가 로그아웃할 때 문제가 있는 경우 명확한 오류 메시지 또는 피드백을 제공합니다. 로그아웃 프로세스가 실패하면 잠재적인 보안 위험이나 데이터 유출을 알립니다.
- 접근성 고려사항: 스크린 리더나 키보드 탐색과 같은 보조 기술을 사용하는 사용자를 비롯해 장애가 있는 사용자가 로그아웃 메커니즘에 액세스할 수 있는지 확인합니다.
- 교차 브라우저 호환성: 다양한 브라우저와 기기에서 로그아웃 기능을 테스트하여 일관되고 안정적으로 작동하는지 확인합니다.
- 지속적인 모니터링 및 업데이트: 잠재적인 취약점이나 보안 허점을 파악하기 위해 로그아웃 프로세스를 정기적으로 모니터링합니다. 확인된 문제를 해결하기 위해 적시에 업데이트와 패치를 구현합니다.
- ID 제휴: 사용자가 제휴 ID를 사용하여 로그인한 경우 ID 공급업체에서도 로그아웃하는 것이 지원되고 필요한지 확인합니다. 또한 ID 제공업체에서 자동 로그인을 지원하는 경우 방지해야 합니다.
권장사항
- 로그아웃 흐름 (또는 기타 액세스 취소 흐름)의 일부로 서버에서 쿠키를 무효화하는 경우 사용자 기기에서도 쿠키를 삭제해야 합니다.
- 사용자 기기에 저장했을 수 있는 민감한 정보(쿠키, localStorage, sessionStorage, indexedDB, CacheStorage, 기타 로컬 데이터 저장소)를 정리합니다.
- 민감한 데이터가 포함된 리소스(특히 HTML 문서)가
Cache-control: no-store
HTTP 헤더와 함께 반환되어 브라우저가 이러한 리소스를 영구 저장소(예: 디스크)에 저장하지 않도록 해야 합니다. 마찬가지로 민감한 데이터를 반환하는 XHR/fetch
호출도 캐싱을 방지하기 위해Cache-Control: no-store
HTTP 헤더를 설정해야 합니다. - 사용자 기기에서 열린 탭이 서버 측 액세스 취소로 최신 상태인지 확인합니다.
로그아웃 시 민감한 정보 정리
로그아웃 시 임시 데이터와 로컬에 저장된 민감한 정보를 삭제하는 것이 좋습니다. 민감한 데이터에 중점을 두는 이유는 모든 데이터를 삭제하면 이 사용자가 다시 돌아올 수 있으므로 사용자 환경이 크게 저하되기 때문입니다. 예를 들어 로컬에 저장된 모든 데이터를 삭제하면 사용자가 쿠키 사용 동의 메시지를 다시 확인하고 웹사이트를 처음 방문한 것처럼 다른 절차를 거쳐야 합니다.
쿠키 정리 방법
로그아웃 상태를 확인하는 페이지의 응답에서 민감한 데이터와 관련이 있거나 민감한 데이터를 포함하는 모든 쿠키를 삭제하는 Set-Cookie
HTTP 헤더를 연결합니다. expires
값을 먼 과거의 날짜로 설정하고 쿠키 값을 빈 문자열로 설정합니다.
Set-Cookie: sensitivecookie1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure
Set-Cookie: sensitivecookie2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure
...
오프라인 시나리오
위에서 설명한 접근 방식은 일반적인 사용 사례에는 충분하지만 사용자가 오프라인으로 작업하는 경우에는 작동하지 않습니다. 로그인 상태를 추적하기 위해 두 개의 쿠키를 요구하는 것이 좋습니다. 하나는 보안 HTTPS 전용 쿠키이고 다른 하나는 JavaScript를 통해 액세스할 수 있는 일반 쿠키입니다. 사용자가 오프라인 상태에서 로그아웃하려고 하는 경우 JavaScript 쿠키를 삭제하고 가능한 경우 다른 정리 작업을 진행할 수 있습니다. 서비스 워커가 있는 경우 Background Fetch API를 활용하여 나중에 사용자가 온라인 상태가 되면 서버에서 상태를 지우는 요청을 다시 시도할 수도 있습니다.
저장용량 정리 방법
로그아웃 상태를 확인하는 페이지의 응답에서 다양한 데이터 스토어의 민감한 정보를 정리해야 합니다.
sessionStorage: 사용자가 웹사이트와의 세션을 종료하면 삭제되지만, 사용자가 웹사이트에서 열린 모든 탭을 닫는 것을 잊을 경우를 대비하여 사용자가 로그아웃할 때 민감한 정보를 선제적으로 정리하는 것이 좋습니다.
// Remove sensitive data from sessionStorage sessionStorage.removeItem('sensitiveSessionData1'); // ... // Or if everything in sessionStorage is sensitive, clear it all sessionStorage.clear();
localStorage, indexedDB, Cache/Service Worker API: 사용자가 로그아웃하면 이러한 API를 사용하여 저장했을 수 있는 민감한 데이터를 정리합니다. 이러한 데이터는 세션 간에 유지되기 때문입니다.
// Remove sensitive data from localStorage: localStorage.removeItem('sensitiveData1'); // ... // Or if everything in localStorage is sensitive, clear it all: localStorage.clear();
// Delete sensitive object stores in indexedDB: const name = 'exampleDB'; const version = 1; const request = indexedDB.open(name, version); request.onsuccess = (event) => { const db = request.result; db.deleteObjectStore('sensitiveStore1'); db.deleteObjectStore('sensitiveStore2'); // ... db.close(); }
// Delete sensitive resources stored with the Cache API: caches.open('cacheV1').then((cache) => { await cache.delete("/personal/profile.png"); // ... } // Or better yet, clear a cache bucket that contains sensitive resources: caches.delete('personalizedV1');
캐시 정리 방법
- HTTP 캐시: 민감한 데이터가 있는 리소스에
Cache-control: no-store
를 설정하면 HTTP 캐시가 민감한 데이터를 유지하지 않습니다. - 뒤로/앞으로 캐시: 마찬가지로
Cache-control: no-store
에 관한 권장사항과 사용자가 로그아웃할 때 민감한 쿠키 (예: 인증 관련 보안 HTTPS 전용 쿠키)를 삭제하는 것에 관한 권장사항을 따른 경우 뒤로/앞으로 캐시에 민감한 데이터가 유지되는 것에 대해 걱정하지 않아도 됩니다. 실제로 뒤로/앞으로 캐시 기능은 다음 신호 중 하나 이상을 관찰하는 경우Cache-control: no-store
HTTP 헤더로 제공되는 동일 출처 페이지를 삭제합니다.- 하나 이상의 보안 HTTPS 전용 쿠키가 수정되거나 삭제되었습니다.
- 페이지에서 발행한 XHR/
fetch
호출에 대한 하나 이상의 응답에Cache-control: no-store
HTTP 헤더가 포함되었습니다.
탭 간 일관된 사용자 환경
사용자가 로그아웃하기 전에 웹사이트의 탭을 많이 열었을 수 있습니다. 그때쯤이면 다른 탭이나 다른 브라우저 창을 잊어버렸을 수도 있습니다. 사용자가 관련 탭과 창을 모두 닫도록 하는 것은 좋지 않습니다. 대신 사용자의 로그인 상태가 탭 간에 일관되도록 하여 사전 대응하세요.
방법
탭 간에 일관된 로그인 상태를 유지하려면 pageshow
/pagehide
이벤트와 Broadcast Channel API를 함께 사용하는 것이 좋습니다.
pageshow
이벤트: 지속된pageshow
에서 사용자의 로그인 상태를 확인하고 사용자가 더 이상 로그인하지 않은 경우 민감한 데이터 또는 전체 페이지를 삭제합니다.pageshow
이벤트는 뒤로/앞으로 탐색에서 복원될 때 페이지가 처음 렌더링되기 전에 트리거되므로 로그인 상태 확인을 통해 페이지를 민감하지 않은 상태로 재설정할 수 있습니다.window.addEventListener('pageshow', (event) => { if (event.persisted && !document.cookie.match(/my-cookie)) { // The user has logged out. // Force a reload, or otherwise clear sensitive information right away. body.innerHTML = ''; location.reload(); } });
브로드캐스트 채널 API: 이 API를 사용하여 탭과 창 간에 로그인 상태 변경사항을 전달합니다. 사용자가 로그아웃한 경우 모든 민감한 데이터를 삭제하거나 민감한 데이터가 있는 모든 탭과 창에서 로그아웃 페이지로 리디렉션합니다.
// Upon logout, broadcast new login state so that other tabs can clean up too: const bc = new BroadcastChannel('login-state'); bc.postMessage('logged out'); // [...] const bc = new BroadcastChannel('login-state'); bc.onMessage = (msgevt) => { if (msgevt.data === 'logged out') { // Clean up, reload or navigate to the sign-out page. // ... } }
결론
이 문서의 안내를 따르면 의도치 않은 로그아웃을 방지하고 사용자의 개인 정보를 보호하는 우수한 로그아웃 사용자 환경을 설계할 수 있습니다.