Token 存储与安全防护

说说看,用户登录后拿到的 Token,前端应该怎么存?

一、Token 存储方案

Token 是用户身份认证的核心凭证,存储方式直接影响安全性。

1. localStorage

最便捷的存储方式,登录成功后,前端通过 localStorage.setItem('token', token) 存储。

使用方式 :请求时手动在 Authorization 头中携带。

优势:持久化存储,关闭浏览器后不丢失,操作简单。

缺点:易受 XSS 攻击(恶意脚本可通过 localStorage 窃取 Token),无法跨域访问(同域名下的不同页面可共享)。


完整方案:Cookie(HttpOnly + Secure) + 防御 CSRF,后端必须部署完善的 CSRF 防护策略**。**

通过后端设置 Set-Cookie 头,将 Token 存入浏览器 Cookie,并配置以下属性:

  • HttpOnly: 设置了 HttpOnly 的 Cookie 无法通过 JavaScript 的 document.cookie API 访问(防止 XSS 攻击窃取 Token)。
  • Secure: 仅在 HTTPS 协议下传输(防止在网络传输中被窃听)。
  • SameSite: 限制跨域请求携带 Cookie(防止 CSRF 攻击,建议设为 SameSite=StrictLax)。
  • Max-Age/Expires: 设置过期时间(避免永久有效)。

使用方式:前端无需手动存储,浏览器会自动在请求头的 Cookie 中携带 Token,前端只需正常调用接口即可。

优势 :彻底避免 XSS 攻击(JS 无法读取),配合 SameSite 可防御 CSRF。

缺点 :跨域请求需后端配合配置 CORS(如 Access-Control-Allow-Credentials: true)。前端无法主动修改 / 删除 Token(需通过后端接口处理)。


3. 双 Token

双 Token 方案通过 "短期业务令牌 + 长期刷新令牌" 的组合,既解决了 localStorage 的 XSS 风险和 Cookie 的 CSRF 风险,又保证了用户体验,是目前 API 认证的最优解之一。

登录阶段:

用户提交账号密码 → 后端验证通过后,返回两个令牌:

  • access_token:短期有效(例如 2 小时),用于请求受保护的 API。
  • refresh_token:长期有效(例如 7 天),仅用于获取新的 access_token,不应具备API访问权限。

存储策略

  • access_token:存入内存(如 Vue/React 的状态管理库、JavaScript 变量)。这样即使被 XSS 窃取,有效期也很短,风险可控。
  • refresh_token :存入 HttpOnly Cookie。因为它有效期长,必须严加保护。由于其本身不直接用于业务请求,即使遭遇 CSRF,攻击者也无法用它来做任何关键操作(只能用来换一个短期的 access_token,而该 access_token 又会因为存在于内存而难以被攻击者获取)。

例:Set-Cookie: refresh_token=xxx; HttpOnly; Secure; SameSite=Strict; Max-Age=604800(7 天有效期)。

无感刷新

access_token 过期后,前端自动处理,用户无感知。

  • 前端通过请求拦截器捕获到 401 响应后,自动发起 "刷新令牌" 的请求(如调用/api/refresh-token接口),浏览器会自动将 refresh_token 从 Cookie 中取出并携带在请求头中。
  • 后端验证并返回新的access_token 前端更新并使用新的access_token

登出阶段:

  • 前端调用登出接口,后端清除服务器端的refresh_token记录,并通过Set-Cookie清除客户端 Cookie 中的refresh_token
  • 前端清除内存中的access_token

二 、JWT

JWT(JSON Web Token)是一种基于 JSON 的轻量级身份认证令牌,用于在客户端和服务器之间安全传递信息。它通过数字签名确保信息的完整性和真实性,常被用于前后端分离、分布式系统中的身份验证和信息交换。

1. JWT 的核心作用

身份认证:用户登录后,服务器生成 JWT 返回给客户端,客户端后续请求携带 JWT,服务器通过验证 JWT 确认用户身份(无需频繁查询数据库校验)。

信息传递:可在 JWT 中嵌入非敏感的用户信息(如用户 ID、角色),减少服务器查询数据库的次数。

2. JWT 组成

JWT 由三部分构成,每部分都是 Base64 编码的字符串(非加密,但可被解码,因此不能存敏感信息),整体格式为:header.payload.signature

Ⅰ. Header(头部)

作用:声明令牌类型(typ: "JWT")和签名算法(如 HS256、RS256)。

处理:Base64 编码后成为 JWT 的第一部分。

Ⅱ. Payload(载荷)

作用:存储非敏感的用户信息(如用户 ID、角色、过期时间),包含标准字段和自定义字段。

Ⅲ. Signature(签名)

作用:防止令牌被篡改,是 JWT 安全性的核心。

生成方式:服务器使用 Header 中声明的算法(如 HS256),结合服务器端密钥(绝对不能泄露给客户端),对前两部分(Header+Payload)进行签名。

验证逻辑:客户端携带 JWT 请求时,服务器会重新计算签名,若与 JWT 中的 signature 一致,则令牌未被篡改且有效。

3. JWT 与 Token 区别

① Token(令牌):是一个抽象概念,指 "用于身份验证或授权的字符串凭证"。任何能证明身份的字符串都可以称为 Token,例如:

Token 的核心作用是:客户端用它来证明 "我是谁",服务器用它来验证身份。

  • 随机生成的一串无意义字符(如 a1b2c3d4...,传统 Session 对应的 SessionID 本质也是一种 Token)。
  • 包含结构化信息的字符串(如 JWT)。

② JWT(JSON Web Token) :是一种 标准化的 Token 格式,它规定了 Token 必须包含三部分(Header + Payload + Signature),且通过 JSON 结构存储信息、通过数字签名保证安全性。

简单说:JWT 是 Token 的 "一种特定格式",它比普通随机字符串 Token 多了 "结构化信息" 和 "签名验证" 的特性。

总结:Token 是统称, 所有用于身份验证的凭证都叫 Token。**JWT 是 Token 的一种,**是一种包含 JSON 信息、带签名的标准化 Token,解决了普通 Token 无法携带信息、难以验证完整性的问题。


三、XSS

1. 什么是 XSS?

XSS(Cross-Site Scripting,跨站脚本攻击)是攻击者在网页中注入恶意脚本(通常是 JavaScript),当用户访问该页面时,恶意脚本会在用户浏览器中执行,从而窃取用户信息(如 Cookie、Token)、篡改页面内容或发起恶意操作。

示例场景

  • 一个论坛允许用户发布内容,攻击者发布包含<script>alert(document.cookie)</script>的评论。
  • 其他用户查看该评论时,恶意脚本会执行,弹出当前用户的 Cookie(若包含登录凭证,攻击者可盗用身份)。

2. 为什么localStorage易受 XSS 攻击?

localStorage是前端 JavaScript 可直接访问的存储对象(通过localStorage.getItem/setItem操作)。

  • 若页面存在 XSS 漏洞,攻击者注入的恶意脚本可直接读取localStorage中的数据(如 Token、用户信息)。
  • 例如:script标签中的恶意代码可通过localStorage.getItem('token')窃取存储的 Token,进而伪造用户身份。

Cookie 的HttpOnly属性是后端设置的安全标识,作用是:禁止前端 JavaScript 通过document.cookie访问该 Cookie

4. 转义

转义可以解决 "恶意脚本注入执行" 的问题,这是 XSS 攻击的核心环节 ------ 如果没有转义,攻击者可以直接注入脚本并执行,进而窃取localStorageCookie(非 HttpOnly)等数据。

论坛如果直接将用户输入的内容 "原样输出" 到页面中,那么包含的 HTML/JavaScript 代码会被浏览器解析为可执行代码,而不是普通字符串,按照 HTML 语法解析。

实现方法:将用户输入中的 HTML 特殊字符 (如 <>&" 等)转义为 "实体字符",让浏览器将其当作普通文本显示,而非可执行代码。

转义只能防止 "用户输入的内容被解析为脚本执行"(如评论区的<script>标签),但无法阻止已经执行的恶意脚本 访问 localStorage,例如假设攻击者通过其他漏洞(如第三方组件漏洞、未过滤的 URL 参数)在页面中注入了恶意脚本。


四、CSRF

1. 什么是 CSRF?

CSRF(Cross-Site Request Forgery,跨站请求伪造):攻击者诱导已登录的用户在不知情的情况下,向目标服务器发送恶意请求(利用用户的登录状态)。由于请求携带用户的 Cookie,服务器会误认为是用户主动操作,从而执行恶意行为(如转账、修改密码)。

示例场景

  • 用户登录了银行网站(bank.com),浏览器保存了银行的登录 Cookie。
  • 攻击者诱导用户访问恶意网站(evil.com),该网站隐藏了一个指向银行转账接口的请求:<img src="https://bank.com/transfer?to=攻击者账号&amount=1000">
  • 用户访问evil.com时,浏览器会自动携带bank.com的 Cookie 发送请求,银行服务器验证 Cookie 有效后,执行转账操作。
2. 如何避免 CSRF 攻击?

核心思路是:让服务器能区分请求是否由用户主动发起,常用方案如下:

防御手段 原理 为什么能避免 CSRF?
1. 验证码 / 二次确认 要求用户手动输入验证码或确认操作(如转账时输入密码)。 攻击者无法绕过用户的手动操作,恶意请求会因缺少验证码而失败。
2. CSRF Token 服务器生成随机 Token(如csrf_token),嵌入页面表单或请求头中。 恶意网站无法获取该 Token(跨域限制),服务器验证请求中是否包含有效 Token,缺失则拒绝。
3. SameSite Cookie 属性 后端设置 Cookie 的SameSite属性(StrictLax)。 限制 Cookie 仅在同域请求中携带,跨域的恶意请求(如evil.combank.com发请求)不会携带 Cookie,服务器无法验证身份。
4. 验证 Referer/Origin 头 服务器检查请求的Referer(来源页面)或Origin(来源域名)是否合法。 恶意请求的Refererevil.com,服务器识别后拒绝处理。

三、总结

攻击类型 核心危害 防御核心手段
XSS 注入恶意脚本,窃取数据 1. 输入过滤 / 输出编码;2. 用HttpOnly保护 Cookie;3. 禁用localStorage存储敏感信息。
CSRF 伪造用户请求,执行恶意操作 1. 使用 CSRF Token;2. 设置SameSite Cookie;3. 验证 Referer/Origin。

相关推荐
laocooon523857886几秒前
创建了一个带悬停效果的“我的个人主页“按钮
前端
2013编程爱好者1 小时前
Vue工程结构分析
前端·javascript·vue.js·typescript·前端框架
小满zs2 小时前
Next.js第十一章(渲染基础概念)
前端
不羁的fang少年3 小时前
前端常见问题(vue,css,html,js等)
前端·javascript·css
change_fate3 小时前
el-menu折叠后文字下移
前端·javascript·vue.js
yivifu3 小时前
CSS Grid 布局详解(2025最新标准)
前端·css
o***Z4485 小时前
前端性能优化案例
前端
张拭心5 小时前
前端没有实际的必要了?结合今年工作内容,谈谈我的看法
前端·ai编程
姜太小白5 小时前
【前端】CSS媒体查询响应式设计详解:@media (max-width: 600px) {……}
前端·css·媒体
HIT_Weston5 小时前
39、【Ubuntu】【远程开发】拉出内网 Web 服务:构建静态网页(二)
linux·前端·ubuntu