良好的登出體驗需要具備什麼條件?

Kenji Baheux
Kenji Baheux

登出

使用者登出網站時,表示他們希望完全擺脫個人化使用者體驗。因此,請務必盡可能貼近使用者的心智模型。舉例來說,適當的登出體驗也應考量使用者在決定登出前可能已開啟的任何分頁。

打造優質的登出體驗,關鍵在於在視覺和狀態方面提供一致的使用者體驗。本指南提供具體建議,說明您應注意哪些事項,以及如何提供良好的登出體驗。

重要考量事項

在網站上導入登出功能時,請留意下列事項,確保登出流程順暢、安全且直覺:

  • 明確且一致的登出使用者體驗:提供明確且一律顯示的登出按鈕或連結,讓使用者在整個網站中輕鬆辨識及存取。避免使用含糊的標籤,或將登出功能隱藏在不明顯的選單、子頁面或其他不易理解的位置。
  • 確認提示:在完成登出程序前,顯示確認提示。這麼做可避免使用者不小心登出,並讓使用者考慮是否真的需要登出,例如,如果他們經常使用強式密碼或其他驗證機制鎖定裝置。
  • 處理多個分頁:如果使用者在不同分頁中開啟同一網站的多個網頁,請務必確保從一個分頁登出時,也能更新該網站所有其他已開啟的分頁。
  • 重新導向至安全的到達網頁:成功登出後,將使用者重新導向至安全的到達網頁,清楚顯示他們已不再登入。請勿將使用者重新導向至含有任何個人化資訊的頁面。同樣地,請確認其他分頁不再顯示已登入的狀態。此外,請確認您並未建立可供攻擊者利用的公開重新導向
  • 工作階段清理:使用者登出後,請徹底移除與使用者工作階段相關聯的任何機密使用者工作階段資料、Cookie 或暫存檔案。這可防止他人未經授權存取使用者資訊或帳戶活動,也能防止瀏覽器從各種快取 (尤其是前進/後退快取) 中還原含有機密資訊的網頁。
  • 錯誤處理和意見回饋:如果使用者在登出時遇到任何問題,請向他們提供清楚的錯誤訊息或意見回饋。如果登出程序失敗,請告知他們可能發生的安全性風險或資料外洩情形。
  • 無障礙考量:請確認身心障礙使用者 (包括使用螢幕閱讀器或鍵盤導覽等輔助技術的使用者) 可以使用登出機制。
  • 跨瀏覽器相容性:在不同瀏覽器和裝置上測試登出功能,確保功能能穩定且可靠地運作。
  • 持續監控及更新:定期監控登出程序,找出任何潛在的安全漏洞或安全漏洞。及時導入更新和修補程式,解決所發現的問題。
  • 身分聯合:如果使用者是使用聯合身分登入,請確認是否支援從身分提供者登出,以及是否需要登出。此外,如果身分識別提供者支援自動登入功能,請務必停用這項功能

正確做法

  • 如果您在登出流程 (或其他存取權撤銷流程) 中,將伺服器上的 Cookie 設為無效,請務必一併刪除使用者裝置上的 Cookie。
  • 清除您可能儲存在使用者裝置上的任何機密資料:Cookie、localStoragesessionStorageindexedDBCacheStorage 和任何其他本機資料儲存空間。
  • 請確認任何含有機密資料的資源 (特別是 HTML 文件) 都會傳回 Cache-control: no-store HTTP 標頭,以免瀏覽器將這些資源儲存在永久性儲存空間 (例如磁碟) 中。同樣地,傳回機密資料的 XHR/fetch 呼叫也應設定 Cache-Control: no-store HTTP 標頭,以免快取。
  • 確認使用者裝置上所有已開啟的分頁都已更新至最新的伺服器端存取權撤銷狀態。

登出時清除機密資料

登出後,請考慮清除暫時性和本機儲存的機密資料。我們之所以著重於敏感資料,是因為清除所有資料會導致使用者體驗大幅惡化,因為這類使用者很可能會回來。舉例來說,如果您清除所有本機儲存的資料,使用者就必須重新確認 Cookie 同意提示,並完成其他程序,就像從未造訪您的網站一樣。

如何清除 Cookie

在確認登出狀態的網頁回應中,附加 Set-Cookie HTTP 標頭,清除所有與機密資料相關或包含機密資料的 Cookie。將 expires 值設為很久以前的日期,並將 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();
    
  • localStorageindexedDBCache/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 via 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.
        // ...
      }
    }
    

結論

只要遵循本文件中的指引,您就能設計出優質的登出使用者體驗,避免使用者不小心登出,並保護使用者的個人資訊。