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

Kenji Baheux
Kenji Baheux

登出

使用者登出網站時,表示想要完全停用個人化的使用者體驗。因此,請務必盡可能遵循使用者的心理模式。舉例來說,如要確保使用者能夠順利登出,應該一併考量使用者可能在登出前開啟的任何分頁。

如要獲得優異的登出體驗,關鍵就在視覺和狀態的各方面,提供一致的體驗。這份指南提供具體建議,協助您瞭解應注意哪些事項,以及如何獲得良好的登出體驗。

重要考量事項

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

  • 清楚一致的登出使用者體驗:提供清楚且一致的登出按鈕或連結,方便使用者在整個網站上輕鬆找到和存取。避免在模糊的選單、子頁面,或其他不當使用中隱藏的狀態下,使用模稜兩可的標籤,或是隱藏登出功能。
  • 確認提示:先實作確認提示,然後再完成登出程序。這有助於避免使用者意外登出,並且讓使用者在確實需要登出的情況下 (例如使用高強度密碼或其他驗證機制) 重新考慮是否要登出。
  • 處理多個分頁:如果使用者在不同分頁中開啟同一個網站的多個網頁,請務必從其中一個分頁登出,一併更新該網站中其他所有開啟的分頁。
  • 重新導向至安全的到達網頁:成功登出後,將使用者重新導向至安全到達網頁,清楚表示他們已經登出。請避免將使用者重新導向至有個人化資訊的頁面。同樣地,請確保其他分頁也不再顯示登入狀態。此外,請勿建構可供攻擊者利用的開放式重新導向
  • 工作階段清理:使用者登出後,徹底移除與使用者工作階段相關聯的所有敏感使用者工作階段資料、Cookie 或暫存檔案。這樣就能防止他人未經授權存取使用者資訊或帳戶活動,也可以防止瀏覽器從各種快取 (尤其是往返快取) 中還原含有機密資訊的網頁。
  • 錯誤處理和意見回饋:在使用者登出時發生問題時,提供清楚的錯誤訊息或意見回饋。請通知使用者,如果登出程序失敗,任何潛在的安全性風險或資料外洩。
  • 無障礙功能注意事項:確保身心障礙使用者可存取登出機制,包括使用螢幕閱讀器或鍵盤導覽等輔助技術的使用者。
  • 跨瀏覽器相容性:在不同瀏覽器和裝置上測試登出功能,確保登出功能穩定可靠。
  • 持續監控及更新:定期監控登出程序,防範任何潛在的安全漏洞或安全性漏洞。及時更新和修補程式,解決任何已發現的問題。
  • 身分聯盟:如果使用者透過聯合身分登入,請查看系統是否支援從識別資訊提供者登出,以及是否需要登出。此外,如果識別資訊提供者支援自動登入,也別忘了禁止該程式

建議做法

  • 如果您是在登出流程 (或其他存取撤銷流程) 過程中將伺服器上的 Cookie 失效,請務必同時刪除使用者裝置上的 Cookie。
  • 清除可能儲存在使用者裝置上的機密資料:Cookie、localStoragesessionStorageindexedDBCacheStorage 以及其他本機資料儲存庫。
  • 確保透過 Cache-control: no-store HTTP 標頭傳回所有含有機密資料的資源 (尤其是 HTML 文件),確保瀏覽器不會將這些資源儲存在永久儲存空間 (例如磁碟中)。同樣地,傳回機密資料的 XHRs/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,並儘可能執行其他清除作業。如果您有 Service Worker,建議您利用 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 (例如與驗證相關的安全 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.
        // ...
      }
    }
    

結論

只要按照本文件中的指南操作,您就能設計完善的登出服務,避免使用者意外登出,同時保護使用者的個人資訊。