3

我正在工作的一个团队在两个不同的域x-ui.app.example.comx-api.app.example.com上有一个 UI 和一个 API 。这种架构显然不适用于 Angular / .NET Core 中的传统 XSRF 方式。

其描述的传统方式是:

  1. 当 x-api 收到它的第一个请求时,或者当 x-ui 调用某个端点时,x-api 将生成一个“XSRF-TOKEN”cookie,如下所述: https ://docs.microsoft.com/en-us/aspnet /core/security/anti-request-forgery?view=aspnetcore-2.2
  2. x-ui 将在以后的数据操作请求(例如 POST)中读取此 cookie,并将在请求标头中包含“XSRF-TOKEN”cookie 的内容:“X-XSRF-TOKEN”

这对我不起作用,因为有两个不同的子域,这意味着不允许 UI 读取由 x-api 子域创建的 cookie。我知道正确的做法是将域折叠到单个 ui/api 域中,然后一切都会神奇地工作,但是,由于时间限制,或者假设我不能这样做,我可以通过添加来解决这个问题app.example.com 域的“XSRF-TOKEN”cookie:

context.Response.Cookies.Append("XSRF-TOKEN", token, new CookieOptions
{
    HttpOnly = false,
    IsEssential = true,
    Secure = true,
    SameSite = SameSiteMode.None,
    Domain = "app.example.com"
});                   

这使 UI 可以访问 cookie,因为它位于父域中。但是我不想这样做,因为这意味着在 app.example.com 中创建的任何其他应用程序也能够看到 cookie,这很粗鲁。

我正在考虑这样做的另一种方法是在初始调用的响应标头中将 XSRF-TOKEN 发送回,此时 UI 将负责将内容作为 cookie 保存在其自己的域中,然后是后续请求可以使用那个cookie。

我的问题是:这是一种可接受的 XSRF 方式吗?通过在 API 的响应标头中传回 XSRF-TOKEN 然后将其作为 cookie 存储在 UI 中,是否存在任何类型的漏洞?

它看起来像这样:

.NET 核心

app.Use(next => context =>
    {
        if (context.Request.Method == HttpMethods.Get)
        {
            var token = antiforgery.GetAndStoreTokens(context).RequestToken;
            context.Response.Headers.Append("XSRF-TOKEN", token);
        }

        return next(context);
    });
CorsPolicyBuilder corsBuilder = new CorsPolicyBuilder();
corsBuilder.AllowAnyHeader();
corsBuilder.AllowAnyMethod();
corsBuilder.WithExposedHeaders(new string[]{ "XSRF-TOKEN" });
corsBuilder.WithOrigins("http://localhost:4200", "null", "x-ui.app.example.com");
corsBuilder.AllowCredentials();
services.AddCors(options => { options.AddPolicy("AllowAll", corsBuilder.Build()); });

还有 Angular 拦截器:(请注意,我会在此处添加更多检查以查看 cookie 是否已设置,因此它不会发生在每个 GET 请求上,而是要明白这一点)。

import {
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
  HttpEvent,
  HttpXsrfTokenExtractor, HttpResponse
} from '@angular/common/http';
import { Observable } from 'rxjs';
import {Injectable} from '@angular/core';
import { CookieService } from 'ngx-cookie-service';
import { tap } from 'rxjs/operators';

@Injectable()
export class HttpXsrfInterceptorService implements HttpInterceptor {
  private cookieValue: string;
  constructor(private tokenExtractor: HttpXsrfTokenExtractor, private cookieService: CookieService) {}
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.method === 'GET') {
      return next.handle(req).pipe(
        tap(evt => {
          if (evt instanceof HttpResponse) {
            console.log(evt.headers.get('XSRF-TOKEN'));
            this.cookieService.set( 'XSRF-TOKEN', evt.headers.get('XSRF-TOKEN'), null, '/', null, false, 'Lax' );
            this.cookieValue = this.cookieService.get('XSRF-TOKEN');
          }
        })
      );
    }

    // We will just forward HEAD, and OPTIONS requests on as usual, without
    // trying to append the X-XSRF-TOKEN header, since XSRF validation isn't done
    // on these methods anyways.
    if (req.method === 'HEAD' || req.method === 'OPTIONS' ) {
      return next.handle(req);
    }

    let requestToForward = req;
    const headerName = 'X-XSRF-TOKEN';

    // Try to extract the 'XSRF-TOKEN' cookie from the request.
    const token = this.tokenExtractor.getToken() as string;

    // If we successfully retrieved a token, we will set a header called 'X-XSRF-TOKEN' which
    // will be used for XSRF validation on the API.
    if (token !== null && !req.headers.has(headerName)) {
      requestToForward = req.clone({setHeaders: {'X-XSRF-TOKEN': token}, withCredentials: true });
    }
    return next.handle(requestToForward);
  }
}
4

0 回答 0