angular icon indicating copy to clipboard operation
angular copied to clipboard

HttpClient does not set X-XSRF-Token on Http Post

Open justin-ruffin opened this issue 8 years ago • 53 comments

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ X] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Updated to angular 5.0.2. Updated from the deprecated HttpModule (@angular/http) to HttpClientModule (@angular/common/http).

Updated Http.post to HttpClient.post and tested. X-XSRF-TOKEN was not present in the Http Header.

HttpClient (@angular/common/http) does not set X-XSRF-Token on Http Post, while Http (@angular/http) does.

Expected behavior

HttpClient should set the X-XSRF-TOKEN on Http Post.

Minimal reproduction of the problem with instructions

Verify that javascript XSRF-TOKEN cookie has been set.

Test HttpClient (@angular/http) and HttpClientModule (@angular/common/http) side by side with nearly identical Http post requests.


//OLD
        CreateOld(sample: Models.Sample): Observable {
            let body = JSON.stringify(sample);
            let headers = new Headers({ 'Content-Type': 'application/json' });
            return this.http
                .post(this.baseUrl + 'api/sample/Create', body, { headers: headers })
                .map(response => { return response.json() as Models.Task[] });
        }
//NEW
	 CreateNew(sample: Models.Sample): Observable {
			let body = JSON.stringify(sample)
			return this.httpClient
				.post(this.baseUrl + 'api/sample/Create', body, { headers: new HttpHeaders().set('Content-Type', 'application/json') });
        }

What is the motivation / use case for changing the behavior?

Environment


Angular version: 5.0.2


Browser:
- [ X] Chrome (desktop) version 62.0.3202.94 
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [X ] Edge version 41.16299.15.0
 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

justin-ruffin avatar Nov 17 '17 20:11 justin-ruffin

Hello, we reviewed this issue and determined that it doesn't fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on StackOverflow using tag angular.

If you are wondering why we don't resolve support issues via the issue tracker, please check out this explanation.


You're probably after https://angular.io/api/common/http/HttpClientXsrfModule

Toxicable avatar Nov 18 '17 06:11 Toxicable

Yes, we already have that implemented. Still an issue.

Per your documentation on HttpClientModule, please see the section in bold. This is either a bug or a invalid documentation.

"Security: XSRF Protection

Cross-Site Request Forgery (XSRF) is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website. HttpClient supports a common mechanism used to prevent XSRF attacks. When performing HTTP requests, an interceptor reads a token from a cookie, by default XSRF-TOKEN, and sets it as an HTTP header, X-XSRF-TOKEN. Since only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.

By default, an interceptor sends this cookie on all mutating requests (POST, etc.) to relative URLs but not on GET/HEAD requests or on requests with an absolute URL.

To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on either"

I have found another bug report #18859, "HttpClient HttpXsrfInterceptor does not set xsrf token for absolute urls" which is a duplicate of this issue.

justin-ruffin avatar Nov 18 '17 07:11 justin-ruffin

I am also facing the same issue with angular 4. I can see the cookie in the response headers but while posting data angular is not adding appropriate headers , also I have tried with HttpClientXsrfModule.

sajagekar avatar Nov 20 '17 10:11 sajagekar

Any update? I have my XSRF-TOKEN non-HttpOnly and angular still does not set X-XSRF-TOKEN

ewaschenko avatar Feb 09 '18 14:02 ewaschenko

am also facing the same issue with Angular 5.2.X version, is there any work around for it. In the first request am getting XSRF-TOKEN cookie in response and in the next requests am expecting to add cookie by angular which is not happening and my XSRF-TOKEN non-HttpOnly.

bhanukumar04 avatar Mar 09 '18 07:03 bhanukumar04

@bhanukumar04 I just created an Interceptor and did it myself.

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        let requestToForward = req;
        let token = this.tokenExtractor.getToken() as string;
        if (token !== null) {
            requestToForward = req.clone({ setHeaders: { "X-XSRF-TOKEN": token } });
        }
        return next.handle(requestToForward);
    }

ewaschenko avatar Mar 09 '18 15:03 ewaschenko

From back end we need to send response with Path=/; HttpOnly=false on first get or login. res.append('Set-Cookie', 'tracking_cookie'=cookie_name; Path=/; HttpOnly=false');

Then writing custom interceptor from Angular to set X-XSRF-TOKEN on each request worked for me.

bhanukumar04 avatar Mar 09 '18 16:03 bhanukumar04

@bhanukumar04 If you look at Angular's xsrf implementation here You can see in the interceptor they have this

if (req.method === 'GET' || req.method === 'HEAD' || lcUrl.startsWith('http://') ||
        lcUrl.startsWith('https://')) {
      return next.handle(req);
    }

If any of the condition are true then adding the header is skipped. That's why I had to make my own since my API is on a different domain.

ewaschenko avatar Mar 09 '18 16:03 ewaschenko

Even when added both HttpClientXsrfModule & HttpClientModule the X-XSRF-TOKEN, still not working. Previously i am on the HttpModule and its working.

irowbin avatar Mar 11 '18 05:03 irowbin

@irowbin make you have your XSRF cookie coming from back end should be HttpOnly=false, then only angular will be able to ready the cookie and send in next requests

bhanukumar04 avatar Mar 11 '18 07:03 bhanukumar04

@bhanukumar04 Yep. i did that already and I am getting that error on get method;

irowbin avatar Mar 11 '18 13:03 irowbin

@bhanukumar04 I have done as you said, about custom angular interceptor to set header. I have issue with first post request. If I send post request first time I get error that my CSRF token is not in cookie. For second time it works good. What can I do with that?

tpredzymyrskyi avatar Mar 13 '18 11:03 tpredzymyrskyi

@tararas124 before your make actual post request , make one get request to get the CSRF cookie, then make actual POST request and this time cookie will be available from the first get request.

bhanukumar04 avatar Mar 13 '18 11:03 bhanukumar04

@bhanukumar04 should I do this inside interceptor? If token is not in cookie, then get request for taking it. Do I understand it right?

tpredzymyrskyi avatar Mar 13 '18 11:03 tpredzymyrskyi

@tararas124 if your are making login request as first request , with successful authentication send the cookie and from the next request it will be available. If you are not making login request make one get request just to get the cookie and then make your post request, this is not necessarily to be done in interceptor.

bhanukumar04 avatar Mar 13 '18 11:03 bhanukumar04

@bhanukumar04 I have done as you said, but It doesn't work. When I send get request before post. They are asynchronous and my post sends faster then get ends. You know what I mean? How to make this calls synchronous? Or some work around Ty so much in advance !

tpredzymyrskyi avatar Mar 13 '18 16:03 tpredzymyrskyi

@tararas124 subscribe to the get call ,then on complete of get call notification, call your post call and that should work http.get(url) .subscribe(() => {}, () => {}, () => { // call you post call here });

bhanukumar04 avatar Mar 13 '18 17:03 bhanukumar04

Angular: 5.2.9 still same problem

rvaliev avatar Mar 25 '18 03:03 rvaliev

Same problem Angular 5.2.5 with Spring boot 1.5.4. I've tried all solutions including writing my own interceptor, but am getting null on let token = this.tokenExtractor.getToken() as string;

by211 avatar Mar 27 '18 16:03 by211

This is working for me. Note that you have to send the cookie from the server with HttpOnly = false or Angular won't be able to read the cookie.

AlejandroFlorin avatar Mar 29 '18 19:03 AlejandroFlorin

Double checked the HttpOnly flag is false through Postman. Something else is blocking the access, not sure what

by211 avatar Mar 30 '18 04:03 by211

Had exact same issue. Frontend runs on path "/". Backend runs on path "/myApp". Backend was setting cookie path as "/myApp". Changing it to "/" fixed my problem.

Klinton90 avatar May 03 '18 03:05 Klinton90

@Klinton90 , when i set path as "/" and HttpOnly flag as false the only angular is able to read the cookie. can you please provide the sample how you are reading cookie from Angular side? Thank you.

bhanukumar04 avatar May 03 '18 07:05 bhanukumar04

I also have this issue, as Angular interceptor ONLY sets X-CSRF-Token header when the request URL is absolute, not relative (as @ewaschenko mentioned).

(https://github.com/angular/angular/blob/2a236b40666c1714cee35efc42bb059761a24a40/packages/common/http/src/xsrf.ts#L81)

Does anybody know why there is such condition?

Olgagr avatar May 22 '18 09:05 Olgagr

Hi ! I am having the same problem. My backend and frontend don't run on the same domain then, because of those conditions, the X-XSRF-TOKEN header is not set. I don't know much about CSRF attacks but I don't see why you would use CSRF protection for cases where backend run on a different domain. Would you consider removing those conditions ? Or at least allow to choose not to apply them based on some configuration ?

JulieRossi avatar May 23 '18 17:05 JulieRossi

@JulieRossi No, they don't consider removing these conditions. Here is a comment from the other issue about that: https://github.com/angular/angular/issues/18859#issuecomment-339463342.

I think the biggest problem is incomplete documentation. It should emphasize this default behaviour of the interceptor. It would remove most of the confusion. After all, writing your own interceptor is easy, so if you need to have this header cross-origin, it's not a problem.

Olgagr avatar May 23 '18 20:05 Olgagr

Ok, thank you @Olgagr for the answer !

JulieRossi avatar May 24 '18 07:05 JulieRossi

@Olgagr Did you only provide your interceptor or also a token extractor?

Using a custom interceptor that reads from the injected document doesn't help for me. It looks like the token extractor is not able to read the cookie received for the second domain.

mikomarrache avatar Jun 12 '18 14:06 mikomarrache

the one doesn't work for absolute url's, there's an active issue -> #18859

WildDev avatar Jul 25 '18 17:07 WildDev

Does anyone can help me in modifying the below code written

As I'm Unable to generate cookies and add the XSRF Token So the XSRF Token must be printed in the backend of my PHP script As I've Seen from the @bhanukumar04 and rest of the users getting successfull

Here is my below code in an Angular 6

import {HttpClient, HttpClientModule, HttpClientXsrfModule, HttpHeaders, HttpParams} from "@angular/common/http";

constructor(private _http:HttpClient) { }
...
apiURL = "//localhost/simple_api/insert.php";

addUser(info){
    return this._http.post(this.apiURL, info, {
      headers:  { 'Content-Type': 'application/x-www-form-urlencoded' },
      withCredentials: true,
      responseType: "arraybuffer",
    }
    )
    .subscribe(
                data => {
                    console.log("POST Request is successful ", data);
                },
                error => {
                    console.log("Error", error);
                }
            ); 
  }

Since I've changed the apiURL = "http://localhost/simple_api/insert.php"; TO apiURL = "//localhost/simple_api/insert.php";

Working Fine. But unable var_dump or print the the headers attached in the PHP script. But I'm getting the form data submitted successfully.

nishanth6 avatar Sep 17 '18 05:09 nishanth6