良好的登出體驗具備哪些條件?

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,並盡可能繼續執行其他清除作業。如果您有 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 (例如與驗證相關的 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.
        // ...
      }
    }
    

結論

請按照本文件中的指南,設計出良好的登出使用者體驗,避免使用者意外登出,並保護使用者的個人資訊。