使用者登出網站時,表示他們需要完全退出個人化使用者體驗。因此,請務必盡可能貼近使用者的心理模型。舉例來說,適當的登出體驗也應考量使用者在決定登出前可能開啟的任何分頁。
如要提供優質的登出體驗,關鍵在於使用者體驗的視覺和狀態方面要保持一致。本指南提供具體建議,說明應注意的事項,以及如何提供良好的登出體驗。
重要考量事項
在網站上導入登出功能時,請注意下列事項,確保登出程序順暢、安全且直覺:
- 清楚一致的登出使用者體驗:提供清楚且持續顯示的登出按鈕或連結,方便使用者在整個網站中輕鬆識別及存取。請勿使用語意不清的標籤,或將登出功能隱藏在不明顯的選單、子網頁或其他不直覺的位置。
- 確認提示:在完成登出程序前,請先實作確認提示。這有助於防止使用者意外登出,並讓使用者重新考慮是否真的需要登出,例如使用者是否勤於以高強度密碼或其他驗證機制鎖定裝置。
- 處理多個分頁:如果使用者在不同分頁中開啟同一網站的多個網頁,請確保從一個分頁登出時,該網站的所有其他開啟分頁也會一併登出。
- 重新導向至安全到達網頁:成功登出後,將使用者重新導向至安全到達網頁,清楚指出使用者已登出。請避免將使用者重新導向至含有任何個人化資訊的頁面。同樣地,請確認其他分頁也不再顯示登入狀態。此外,請確保您不會建構出攻擊者可利用的開放式重新導向。
- 清除工作階段:使用者登出後,完全移除與使用者工作階段相關聯的任何機密使用者工作階段資料、Cookie 或暫時檔案。這樣一來,未經授權者就無法存取使用者資訊或帳戶活動,瀏覽器也不會從各種快取 (尤其是返回/前進快取) 還原含有私密資訊的網頁。
- 錯誤處理和意見回饋:如果使用者登出時發生任何問題,請提供清楚的錯誤訊息或意見回饋。如果登出程序失敗,請告知他們任何潛在的安全風險或資料外洩情形。
- 無障礙考量:確保登出機制可供失能使用者存取,包括使用螢幕閱讀器或鍵盤導覽等輔助技術的使用者。
- 跨瀏覽器相容性:在不同瀏覽器和裝置上測試登出功能,確保功能運作一致且穩定。
- 持續監控及更新:定期監控登出程序,找出任何潛在安全漏洞或安全漏洞。及時更新及修補,解決發現的問題。
- 身分聯盟:如果使用者是透過聯合身分登入,請確認是否支援從身分提供者登出,以及是否需要這麼做。此外,如果身分識別提供者支援自動登入,請務必禁止這項功能。
DO
- 如果您在伺服器上使 Cookie 失效 (做為登出流程或其他存取權撤銷流程的一部分),請務必一併刪除使用者裝置上的 Cookie。
- 清除可能儲存在使用者裝置上的任何私密資料:Cookie、localStorage、sessionStorage、indexedDB、CacheStorage 和任何其他本機資料儲存空間。
- 請確保傳回任何含有私密資料的資源 (尤其是 HTML 文件) 時,都附上
Cache-control: no-store
HTTP 標頭,這樣瀏覽器就不會將這些資源儲存在永久儲存空間 (例如磁碟) 中。同樣地,傳回私密資料的 XHR/fetch
呼叫也應設定Cache-Control: no-store
HTTP 標頭,防止任何快取。 - 確保使用者裝置上開啟的所有分頁,都已根據伺服器端撤銷的存取權更新。
登出時清除機密資料
登出後,請考慮清除暫時和本機儲存的私密資料。我們著重處理私密資料,是因為如果清除所有資料,使用者體驗會大幅變差,因為使用者很可能會回來。舉例來說,如果您清除所有本機儲存的資料,使用者就必須重新確認 Cookie 同意提示,並完成其他程序,就像從未造訪過您的網站一樣。
如何清除 Cookie
在確認登出狀態的網頁回應中,附加 Set-Cookie
HTTP 標頭,清除所有與機密資料相關或包含機密資料的 Cookie。將 expires
值設為遠古時期的日期,並將 Cookie 值設為空白字串,確保 Cookie 遭到刪除。
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
...
離線情境
上述方法足以應付一般用途,但使用者離線時就無法運作。您不妨考慮要求使用兩個 Cookie 來追蹤登入狀態:一個是僅限 HTTPS 的安全 Cookie,另一個是可透過 JavaScript 存取的普通 Cookie。如果使用者嘗試在離線時登出,您可以清除 JavaScript Cookie,並盡可能繼續執行其他清除作業。如果您有服務工作站,也可以利用 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
的建議,並在使用者登出時清除私密 Cookie (例如與驗證相關的 HTTPS 專用安全 Cookie),就不必擔心私密資料會保留在返回/快取中。事實上,如果往返快取功能觀察到下列一或多個信號,就會從提供Cache-control: no-store
HTTP 標頭的同源網頁中逐出:- 一或多個僅限 HTTPS 的安全 Cookie 已修改或刪除。
- 網頁發出的 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(); } });
Broadcast Channel 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. // ... } }
結論
只要遵循本文的指引,就能設計出優質的登出使用者體驗,避免使用者意外登出,並保護使用者的個人資訊。