怎样才算是良好的退出体验?

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 值设置为空字符串以准确衡量效果。

<ph type="x-smartling-placeholder">
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 标头提供的同源网页: <ph type="x-smartling-placeholder">
      </ph>
    • 一个或多个安全的 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.
        // ...
      }
    }
    

总结

按照本文档中的指导,您可以设计出色的退出用户体验,以防止用户意外退出,并保护用户的个人信息。