SPA(Angular 5)+ASP.NET Core Web API的XSS/CSRF防护及防伪令牌问询
嘿,刚好在Angular + ASP.NET Core Web API的跨域项目里折腾过这些安全问题,给你分享下生产环境里的成熟实践和常见方案:
1. 同时抵御XSS与CSRF的成熟实践
其实核心思路是结合两种存储方式的优势,规避各自的风险,目前业界主流的方案是「内存存储Access Token + HttpOnly Cookie存储Refresh Token」,再配合CSRF验证和XSS防护措施,具体拆解如下:
核心存储策略
- Access Token 存在内存中:不要用
localStorage或sessionStorage,因为这两个存储会被XSS脚本直接读取。把token存在Angular服务的私有变量里(比如AuthService中的private _accessToken: string),页面刷新后token会丢失,但可以通过Refresh Token重新获取——好在Access Token有效期通常设置得很短(比如15分钟),就算被劫持,影响窗口也很小。 - Refresh Token 存在HttpOnly Cookie中:给Cookie加上
HttpOnly(禁止JS读取)、Secure(仅HTTPS传输)、SameSite=Strict(跨域请求不携带Cookie,直接阻断大部分CSRF场景)属性,有效期可以设长一点(比如7天)。
认证流程细节
- 用户登录时,后端验证身份后,返回短有效期的Access Token,同时设置上述属性的Refresh Token Cookie。
- SPA把Access Token存在内存变量里,后续所有API请求都在
Authorization头里带上Bearer {accessToken}。 - 当Access Token过期,SPA调用专门的刷新接口(比如
/api/auth/refresh)——这个请求会自动携带Refresh Token Cookie,同时需要带上CSRF令牌(后面会讲怎么获取)。 - 后端验证Refresh Token有效且CSRF令牌合法后,返回新的Access Token,SPA更新内存中的token,继续正常请求。
配套防护措施
- XSS防护:除了内存存token,还要严格做用户输入/输出编码,启用Content Security Policy (CSP),禁止内联脚本、限制资源加载来源,从根源减少XSS攻击的可能。
- CSRF防护:虽然
SameSite=Strict能防大部分场景,但对于同域或允许的跨域请求,还要配合双重提交Cookie模式:后端设置一个非HttpOnly的XSRF-TOKENCookie,SPA读取这个Cookie后,在请求头里带上X-XSRF-TOKEN,后端验证两者是否一致。ASP.NET Core默认支持这个机制,只要简单配置就能生效。
至于你提到的portal.azure.com,他们大概率也是用的这个思路:Access Token存在内存里(页面刷新会重新登录或用Refresh Token获取),请求时放在Authorization头中,Refresh Token则藏在HttpOnly Cookie里,既规避了XSS风险,又通过SameSite和CSRF验证防住了CSRF。
2. SPA与Web API获取防伪令牌的常见实践
目前最常用的是自动Cookie+请求头绑定的方式,Angular和ASP.NET Core原生就支持,几乎不需要额外代码:
后端配置(ASP.NET Core)
- 在
Startup.cs(.NET 5及之前)或Program.cs(.NET 6+)中配置防伪服务:
services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN"; // 指定请求头名称,和Angular默认一致 });
- 当用户访问SPA的入口页面时,生成并存储防伪令牌Cookie:
app.Use(async (context, next) => { // 假设SPA入口是index.html if (context.Request.Path.Value == "/index.html") { var antiforgery = context.RequestServices.GetRequiredService<IAntiforgery>(); await antiforgery.GetAndStoreTokensAsync(context); // 这会自动设置名为XSRF-TOKEN的非HttpOnly Cookie } await next(); });
SPA端(Angular)
Angular的HttpClient会自动读取名为XSRF-TOKEN的Cookie,并在所有非GET的请求(POST/PUT/DELETE等)的头里带上X-XSRF-TOKEN,完全不需要手动处理——只要后端配置正确,就能自动完成CSRF令牌的验证。
备选方案:主动请求获取令牌
如果SPA和API完全分离(比如部署在不同域名,且无法通过入口页面设置Cookie),可以专门提供一个GET接口(比如/api/auth/get-xsrf-token),后端在这个接口中生成防伪令牌并返回(或设置Cookie),SPA调用接口后把令牌存在内存里,后续请求手动在头里带上。不过这种方式不如自动绑定方便,一般只在特殊场景下使用。
内容的提问来源于stack exchange,提问作者DavidBL

