20250706-前端登录token到底应该存在哪?LocalStorage、SessionStorage还是Cook

原文摘要

如果你做过任何需要登录的功能,那么你一定思考过这个问题:当后端甩给我一个token时,我一个前端,到底应该把它放在哪儿?

这个问题看似简单,无非就是 LocalStorageSessionStorageCookie 三个选项。但如果我告诉你,一个错误的选择,可能会直接导致你的网站出现严重的安全漏洞,你是不是会惊出一身冷汗?

许多开发者(包括曾经的我)不假思索地把token塞进LocalStorage,因为它的API最简单好用。但这种方便的背后,隐藏着巨大的风险。

今天,这篇文章将带你彻底终结这个纠结。我们将深入对比这三位“候选人”的优劣,剖析它们各自面临的安全威胁(XSSCSRF),并最终给出一个当前业界公认的最佳实践方案。


1. 三种存储方案对比

在做决定前,我们先来快速了解一下这三个Web存储方案的基本特性。

特性LocalStorageSessionStorageCookie
生命周期永久,除非手动清除页面会话期间(标签页关闭即失效)可设置过期时间
存储大小约 5MB约 5MB约 4KB
JS可访问性可访问可访问可访问(除非设置HttpOnly
与服务端通信不会自动发送不会自动发送每次HTTP请求都会自动携带

一目了然,LocalStorageSessionStorage是HTML5提供的新API,更大、更易用。而Cookie是“老前辈”,小而精,并且有个独一无二的特性:会自动“粘”在HTTP请求头里发给后端。


2. 两大安全攻击 XSS 与 CSRF

选择存储方案,本质上是在权衡安全和便利。而威胁token安全的主要是下面两种。

XSS (跨站脚本攻击)

  • 手法:攻击者通过某种方式(比如评论区)向你的网站注入了恶意的JavaScript脚本。当其他用户访问这个页面时,这段脚本就会执行。
  • 目标:如果你的token存在LocalStorageSessionStorage里,那么这段恶意脚本就可以通过简单的localStorage.getItem('token')轻松地把它偷走,然后发送到攻击者的服务器。token失窃,你的账户就被冒充了。

结论一:LocalStorageSessionStorage 对 XSS 攻击是完全不设防的。只要你的网站存在XSS漏洞,存在里面的任何数据都能被轻易窃取。

CSRF (跨站请求伪造)

  • 手法:你刚刚登录了你的银行网站bank.com,你的登录凭证(Cookie)被浏览器记住了。然后,你没有关闭银行页面,而是点开了一个恶意网站hacker.com。这个恶意网站的页面里可能有一个看不见的表单或<img>标签,它会自动向bank.com/transfer这个地址发起一个转账请求。
  • 目标:因为浏览器在发送请求到bank.com时,会自动带上bank.comCookie,所以银行服务器会认为这个请求是你本人发起的,于是转账就成功了。你神不知鬼不觉地被“伪造”了意愿。

结论二:Cookie 如果不加以保护,会受到 CSRF 攻击的威胁。


3. 现代Cookie的“优势”

看到这里你可能会想:LocalStorage防不住XSS,Cookie防不住CSRF,这可怎么办?

别急,我们的Cookie经过多年的进化,已经有了强大的防止手段。

HttpOnly - 封印JS的访问

如果在设置Cookie时,加上HttpOnly属性,那么通过JavaScript(如 document.cookie)将无法读取到这个Cookie

Set-Cookie: token=...; HttpOnly

这意味着,即使网站存在XSS漏洞,攻击者的恶意脚本也偷不走这个Cookie,从根本上阻断了XSS利用token的路径。

SameSite - 防止携带

SameSite属性用来告诉浏览器,在跨站请求时,是否应该携带这个Cookie。它有三个值:

  • Strict:最严格。只有当请求的发起方和目标网站完全一致时,才会携带Cookie,能完全防御CSRF。
  • Lax:比较宽松(现在是大多数浏览器的默认值)。允许在“顶级导航”(如<a>链接、GET表单)的跨站请求中携带Cookie,但在<img><iframe>、POST表单等“嵌入式”请求中会拦截。这已经能防御大部分CSRF攻击了。
  • None:最松。任何情况下都携带Cookie。但必须同时指定Secure属性(即Cookie只能通过HTTPS发送)。

对于登录token,我们通常希望它尽可能安全,所以SameSite=Strict是最佳选择。

Secure - 保证传输安全

这个属性很简单,只要设置了它,Cookie就只会在HTTPS的加密连接中被发送,可以防止在传输过程中被窃听。


4. 终极答案

综合以上所有分析,我们终于可以给出当前公认的最佳、最安全的方案了。

这个方案的核心是“组合拳”:将不同生命周期的token存放在不同的地方,各司其职。

我们通常有两种token

  • AccessToken:生命周期很短(如15分钟),用于访问受保护的API资源。
  • RefreshToken:生命周期很长(如7天),专门用来在AccessToken过期后,换取一个新的AccessToken

最佳存储策略如下:

  1. RefreshToken: 存放在一个 HttpOnly=true, Secure=true, SameSite=StrictCookie中。

    • 为什么? RefreshToken非常关键且长期有效,所以必须用最安全的方式存储。HttpOnly让它免受XSS攻击,SameSite=Strict让它免受CSRF攻击。前端 JS 完全接触不到它,只在需要刷新token时,由浏览器自动带着它去请求/refresh_token这个特定接口。
  2. AccessToken: 存放在 JavaScript的内存中(例如,一个全局变量、React Context或Vuex/Pinia等状态管理库里)。

    • 为什么? AccessToken需要被JS读取,并放在HTTP请求的Authorization头里(Bearer xxx)发送给后端。将它放在内存中,可以避免XSS直接从LocalStorage里扫荡。当用户关闭标签页或刷新页面时,内存中的AccessToken会丢失。
    • 丢失了怎么办? 这就是RefreshToken发挥作用的时候了。当应用启动或AccessToken失效时,我们就向后端发起一个请求(比如访问/refresh_token接口),浏览器会自动带上我们安全的RefreshToken Cookie,后端验证通过后,就会返回一个新的AccessToken,我们再把它存入内存。

这个方案完美地结合了安全性和可用性,几乎无懈可击。

一张表格说透

存储方式优点缺点(安全风险)推荐用法
LocalStorageAPI简单,容量大,持久XSS不推荐存储敏感信息(如Token)
SessionStorageAPI简单,标签页关闭即删XSS同上
Cookie可自动发送,可配置安全属性CSRF (若无SameSite)不推荐直接存AccessToken
内存 + HttpOnly Cookie安全 (防XSS+CSRF), 体验好方案略复杂最佳实践 (AccessToken存内存,RefreshTokenHttpOnly Cookie)

希望这篇文章能彻底帮你理清思路。当你在实践中或者面试被问到时,就可以把这套“方案”发挥出来。

谢谢大家🙂

📌 你可以继续看我的系列文章

原文链接

进一步信息揣测

  • LocalStorage的致命缺陷:尽管API简单易用,但存储的token会完全暴露给XSS攻击,恶意脚本可轻松通过localStorage.getItem窃取,业内已将其视为高风险方案。
  • Cookie的进化内幕:现代Cookie通过HttpOnlySameSiteSecure三属性组合可同时防御XSS和CSRF,但需后端显式设置(如Set-Cookie: token=...; HttpOnly; SameSite=Strict; Secure),普通文档很少强调配置细节。
  • SameSite的默认陷阱:浏览器默认将SameSite设为Lax虽能防御大部分CSRF,但对敏感操作(如转账)仍需手动设为Strict,否则仍可能通过GET请求触发攻击,这一细节常被忽略。
  • Token分存的行业实践:高安全场景下,业内会拆分access_token(短时效,存HttpOnly Cookie)和refresh_token(长时效,存后端内存或数据库),但前端文档通常只提基础方案。
  • XSS的深层威胁:即使使用HttpOnly Cookie,若网站存在XSS漏洞,攻击者仍可通过模拟用户操作(如自动提交表单)间接利用token,彻底防御需配合CSP等额外措施。
  • CSRF的隐蔽风险:SameSite=Lax下,GET请求仍可能携带Cookie,若后端API设计不当(如用GET执行修改操作),会留下CSRF漏洞,需强制遵循RESTful规范。
  • SessionStorage的误用真相:虽在标签页关闭后失效,但同一标签页内若被注入XSS脚本,token仍会被盗,安全性与LocalStorage无异,仅适合临时非敏感数据。
  • Token存储的权衡本质:安全方案选择本质是“攻击面转移”,无绝对完美方案,需根据业务场景调整(如SPA常用内存存储+短时效token降低XSS影响)。