单页渐进式Web应用用户名+密码+TOTP MFA登录方案咨询
TOTP+账号密码MFA登录方案的安全与实现疑问
背景
从事后端工作数年后回归全栈项目开发,我们需要实现基于TOTP的用户名+密码+MFA登录功能,不依赖第三方身份提供商。应用是可安装到移动设备主屏幕的单页渐进式Web应用(PWA),后端为单体架构,前端设有API Gateway服务,且独立部署AuthN服务(拆分AuthN服务的目的是将认证数据与应用数据存储在独立数据库中,通过网络层面限制访问权限,降低SQLi等攻击的风险面)。
我考虑采用Refresh Token + JWT Access Token的方案:AuthN服务将Refresh Token以Cookie形式返回,Access Token包含在响应payload中;前端从localStorage获取Access Token,请求时携带Authorization: Bearer <token>头,通过Cookie中的Refresh Token获取新的Access Token。
问题列表
- 该方案存在哪些安全漏洞?
- 是否有更优、更标准、更安全的实现方式?
- 该方案是否符合相关标准?是否模仿了OAuth2/OIDC的部分机制?
- 本应用规模较小(每分钟请求量不足10次),使用Access Token是否有意义?为何不每次请求都携带Refresh Token?
问题解答
1. 当前方案的安全漏洞
- LocalStorage存储Access Token的风险:LocalStorage属于前端可访问存储,易受XSS攻击。攻击者通过注入恶意脚本窃取Access Token后,可直接用
Authorization头发起合法请求,冒充用户操作。 - Refresh Token的Cookie配置隐患:若Cookie未配置
HttpOnly、Secure、SameSite=Strict等安全属性,会面临CSRF攻击风险,或被前端脚本窃取(无HttpOnly时);未设Secure的Cookie可能在HTTP连接中传输,被中间人拦截。 - Access Token泄露后的滥用窗口:若Access Token未设置较短过期时间,一旦泄露,攻击者有更长时间进行未授权操作;而前端直接持有Access Token的模式,本身就提升了泄露风险。
- Refresh Token缺少失效机制:若未实现Refresh Token主动失效(比如单设备登录时旧Token失效、主动登出时销毁Token),一旦Refresh Token泄露,攻击者可持续获取新Access Token,直到Token自然过期。
2. 更优更标准的实现方式
- 将Access Token存入安全Cookie:把Access Token也存入带
HttpOnly、Secure、SameSite=Strict属性的Cookie,让浏览器自动在请求时携带,避免XSS窃取风险;同时配合CSRF Token防护CSRF攻击。 - 短生命周期Access Token+长生命周期Refresh Token:Access Token设15分钟左右的短过期时间,Refresh Token设较长时间但支持主动失效(比如存储在AuthN服务数据库中,登出或异地登录时标记失效)。
- Refresh Token轮换机制:每次用Refresh Token获取新Access Token时,同步返回新的Refresh Token,旧Token立即失效。即使Refresh Token泄露,攻击者也只能使用一次,降低危害。
- MFA会话加固:完成TOTP验证后,给会话标记MFA已验证状态,后续敏感操作(如修改密码、查看敏感数据)再次校验该状态,提升安全性。
3. 方案与标准的关系
该方案确实模仿了OAuth2/OIDC的部分核心机制:
- 沿用了OAuth2中Access Token用于资源授权访问、Refresh Token用于获取新Access Token的核心逻辑。
- 但不完全符合OAuth2/OIDC的标准流程:OAuth2针对SPA的最佳实践是使用Authorization Code Flow with PKCE,不会让前端直接存储Access Token;OIDC在OAuth2基础上增加了标准化身份认证流程(如ID Token组件),你的方案未涉及这些部分。
4. 小流量场景下使用Access Token的意义
即使请求量小,使用Access Token依然有必要:
- 降低高权限Token暴露风险:Refresh Token权限更高,可持续获取新Access Token,若每次请求都携带,会大幅增加其在网络传输中的暴露次数,一旦泄露危害极大;Access Token权限低、生命周期短,泄露后的影响范围和时长有限。
- 减少AuthN服务负载:每次请求都调用AuthN服务校验Refresh Token,会增加其压力;使用Access Token的话,API Gateway或后端服务可直接校验JWT签名(无需查询数据库),提升处理效率。
- 权限粒度控制:Access Token可携带细粒度权限信息(如用户角色、可访问资源范围),后端服务直接解析即可完成权限校验;而Refresh Token通常仅用于获取新Token,不携带权限信息。
- 架构可扩展性:养成短生命周期Access Token的实践习惯,未来应用流量增长时,无需重构认证逻辑,保持架构的可扩展性。
内容的提问来源于stack exchange,提问作者user2268997
相关产品推荐
相关产品推荐

