不使用Cookie时,JWT的access/refresh token存储及安全方案咨询
针对你遇到的XSS攻击下refresh token被滥用的问题,以下是几种无需依赖Cookie的JWT安全实现思路:
1. 短生命周期Refresh Token + 令牌轮换+ 单次有效性校验
- 把refresh token的有效期大幅缩短(比如15分钟以内),同时要求每次调用
/refresh接口时,仅允许当前数据库中存储的有效refresh token换取新令牌对,并且立即将旧refresh token标记为失效。 - 每次刷新成功后,返回新的access token(有效期保持短时长,比如5-15分钟)和新的refresh token,前端替换本地存储的旧令牌。
- 这种方式下,即使攻击者通过XSS拿到refresh token,最多只能在15分钟内使用一次,之后旧令牌失效,新令牌只有用户正常操作时才能获取,攻击者无法持续滥用。
2. 内存级令牌存储替代localStorage
- 避免将refresh token存在持久化的localStorage中,改用前端内存存储:比如Vuex/Redux的全局状态、或者内存中的全局变量。
- 内存存储的令牌会在页面刷新、标签关闭后立即消失,即使发生XSS攻击,攻击者也只能在当前会话存续期间获取令牌,无法长期留存。
- 缺点是用户刷新页面后需要重新登录,可结合会话存储(sessionStorage)做兼容,但sessionStorage仍可被XSS读取,仅能在标签页关闭后失效。
3. 令牌与客户端标识绑定
- 生成refresh token时,将其与客户端的唯一标识(比如前端生成的设备ID、浏览器UA+IP段的哈希值)绑定,存储在数据库中。
- 调用
/refresh接口时,前端需要同时提交该客户端标识,后端校验标识与数据库中存储的一致才允许刷新。 - 即使XSS拿到refresh token,攻击者在其他设备或环境下无法匹配客户端标识,无法滥用令牌;但同一浏览器内的XSS仍能绕过,需配合其他方案使用。
4. 异常触发二次验证
- 配置后端规则:当refresh token的使用场景出现异常时(比如IP地址突变、UA变化、短时间内多次刷新令牌),强制要求用户完成二次验证(比如短信验证码、滑动验证、生物识别)。
- 这种方式能在攻击者尝试滥用令牌时触发拦截,即使令牌被窃取,也无法绕过二次验证获取新令牌。
5. 前端加密存储令牌
- 对存储的access和refresh token进行对称加密(比如AES),加密密钥仅存于前端内存(比如Vuex状态),不做持久化存储。
- 页面刷新后密钥丢失,即使localStorage中存有加密后的令牌,也无法解密使用;XSS攻击只有在当前会话存续期间获取到内存中的密钥,才能解密令牌。
基础防护:严格的XSS风险管控
所有令牌存储方案都无法完全规避XSS风险,因此必须做好前端基础防护:
- 启用内容安全策略(CSP),限制脚本加载来源,禁止执行内联脚本。
- 对所有用户输入进行严格过滤和转义,避免DOM型XSS。
- 避免使用
eval、innerHTML等危险API,改用安全的DOM操作方式。
内容的提问来源于stack exchange,提问作者William Le
相关产品推荐
相关产品推荐

