angular4 httpclient csrf 不发送 x-xsrf-token

Posted

技术标签:

【中文标题】angular4 httpclient csrf 不发送 x-xsrf-token【英文标题】:angular4 httpclient csrf does not send x-xsrf-token 【发布时间】:2018-02-12 22:05:10 【问题描述】:

在 Angular 文档中,提到了 Angular httpclient 会在 post 请求的标头 X-XSRF-TOKEN 中自动发送 cookie XSRF-TOKEN 的值。 Documentation link

但它不会为我发送标题。这是我的代码

设置 cookie 的 Nodejs 代码

router.get('/set-csrf',function(req,res,next)
    res.setHeader('Set-Cookie', "XSRF-TOKEN=abc;Path=/; HttpOnly; SameSite=Strict");    
    res.send();
  )

我在 app.module.ts 中使用过 httpclient

imports: [
  HttpClientModule
]

** 以上代码仅用于调试目的。我没有 set-csrf 端点。

但是当我发送 post 请求时它不会发送任何标题。我无法调试。

我也在 Angular 的 github 存储库中添加了这个问题。 HttpXsrfInterceptor 检查请求是 GET 还是 HEAD,或者它是否以 http 开头。如果为 true,则跳过添加标题。

这是HttpXsrfInterceptor class中的代码

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
    const lcUrl = req.url.toLowerCase();
    // Skip both non-mutating requests and absolute URLs.
    // Non-mutating requests don't require a token, and absolute URLs require special handling
    // anyway as the cookie set
    // on our origin is not the same as the token expected by another origin.
    if (req.method === 'GET' || req.method === 'HEAD' || lcUrl.startsWith('http://') ||
        lcUrl.startsWith('https://')) 
      return next.handle(req);
    
    const token = this.tokenService.getToken();

    // Be careful not to overwrite an existing header of the same name.
    if (token !== null && !req.headers.has(this.headerName)) 
      req = req.clone(headers: req.headers.set(this.headerName, token));
    
    return next.handle(req);
  

我不确定他们为什么跳过 http/s 部分。这是我的issue in github

【问题讨论】:

你使用 CORS 请求吗? 我正在添加标题"Access-Control-Allow-Headers","*" 我看到了你的问题。在我看来,角度应该以不同的方式处理 http[s] 链接。例如,分别存储每个域的最后一个 csrf 令牌。让另一个拦截器绞盘以这种方式处理 csrf 是否很好? 我不明白的一件事(我在github问题中也提到过)为什么http url需要特殊处理?因为 owasp 指南和***文章都没有提到任何此类情况。你提到为每个域存储 csrf 令牌,你能解释一下这个 http/https 和多个域吗?我猜这仅适用于多域场景(子域),但它与以 http/https 开头的 url 有什么关系? 我的意思是,如果 url 以 http/https Angular 应用程序调用后端在不同的资源上,加载的 Angular 应用程序不同。它可以是子域场景,甚至可以是允许 CORS 访问的完全不同的站点。因此,如果它是不同的域,他们每个人都不能互相认识,并且他们分别发送 csrf 令牌。因此,如果 Angular 应用程序看到 url 以 http 开头,则不应发送从不以 http 开头的 url 获得的 csrf 令牌,因为它可能导致泄露令牌。而不是这个应用程序应该跟踪由域分割的 csrf 令牌并将令牌发送到从中获取它的域。 【参考方案1】:

您正在寻找的是HttpClientXsrfModule

请在此处阅读更多信息:https://angular.io/api/common/http/HttpClientXsrfModule。

你的用法应该是这样的:

imports: [   
 HttpClientModule,  
 HttpClientXsrfModule.withOptions(
   cookieName: 'My-Xsrf-Cookie', // this is optional
   headerName: 'My-Xsrf-Header' // this is optional
 ) 
]

此外,如果您的代码通过绝对 URL 定位 API,默认 CSRF 拦截器将无法开箱即用。相反,您必须实现自己的拦截器,它不会忽略绝对路由。

@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor 

  constructor(private tokenExtractor: HttpXsrfTokenExtractor) 
  

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
    const headerName = 'X-XSRF-TOKEN';
    let token = this.tokenExtractor.getToken() as string;
    if (token !== null && !req.headers.has(headerName)) 
      req = req.clone( headers: req.headers.set(headerName, token) );
    
    return next.handle(req);
  

最后将其添加到您的提供商:

providers: [
   provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptor, multi: true 
]

【讨论】:

它会将 CSRF 令牌添加到所有请求还是仅添加数据更改请求(POST、PUT、DELETE)? 您使用 GET 获取 CSRF 令牌,因此即使您没有通过它,GET 也会为您返回它(当然关于用户身份验证),并且从那里每次请求都会携带它 - 由于 POST、PUT、DELETE、PATCH 正在更改数据,因此拥有 CSRF 至关重要。 我的意思是,使用您提供的代码,所有请求都将设置令牌,甚至是 GET 请求。我不认为这是故意的。 @Miroslav - 我不明白你对绝对和相对路径的评论。在 Web 服务中,路径不总是绝对的吗?否则,客户端如何知道服务器在哪里(或者客户端是否从初始 GET 的响应中存储服务器的信息)? 您的解决方案对我有用。非常感谢。我将url 从绝对更改为相对。虽然我不明白为什么绝对和相对之间的行为不同。你能解释一下吗?【参考方案2】:

我想正确的方法是withOptions。我使用 withConfig 并收到错误 Property 'withConfig' does not exist on type 'typeof HttpClientXsrfModule'. 这是文档中的打字问题。您需要使用“withOptions”而不是HttpClientXsrfModule.withOptions( cookieName: 'My-Xsrf-Cookie', headerName: 'My-Xsrf-Header', )

【讨论】:

【参考方案3】:

使用最近的 Angular 版本,我遇到了以下问题。当令牌使用标头名称“XSRF-TOKEN”传递给客户端时,响应必须使用标头名称“X-XSRF-TOKEN”反馈令牌。所以这里是 Miroslav 上面代码的一个稍微修改过的版本,它适用于我。

@Injectable()
export class HttpXSRFInterceptor implements HttpInterceptor 

  constructor(private tokenExtractor: HttpXsrfTokenExtractor) 
  

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
    const headerName = 'XSRF-TOKEN';
    const respHeaderName = 'X-XSRF-TOKEN';
    let token = this.tokenExtractor.getToken() as string;
    if (token !== null && !req.headers.has(headerName)) 
      req = req.clone( headers: req.headers.set(respHeaderName, token) );
    
    return next.handle(req);
  

【讨论】:

以上是关于angular4 httpclient csrf 不发送 x-xsrf-token的主要内容,如果未能解决你的问题,请参考以下文章

Angular4从Http迁移到HttpClient - 使用凭据

Angular 4 HttpClient 查询参数

Binance API 和 Angular 4 httpClient

Angular 4.3.3 HttpClient:如何从响应的标头中获取值?

Angular 4 错误:没有 HttpClient 的提供者

Angular 4 无法在标题中设置 CSRF-TOKEN