Update for angular 5.0+
Http
service beign deprecated in favor of HttpClient
, the CookieXSRFStrategy
class has been deprecated too, now this mission is delegated to the HttpClientXsrfModule
class. If you want to customize the header and cookie names, you just need to import this module like so :
@NgModule({
imports: [
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'My-Xsrf-Cookie',
headerName: 'My-Xsrf-Header',
}),
]
})
export class MyModule{}
For future readers:
Ajax response cookies
You cannot access any cookie from any Ajax response, if you check the XHR spec, you will notice that any access to a header matching "Set-Cookie" is forbidden :
A forbidden response-header name is a header name that is a byte-case-insensitive match for one of:
But my cookie isn't httpOnly
Good for you, but httpOnly
only states that your cookie cannot be accessed via document.cookie
(see further).
The document.cookie
API
The only cookies you will be able to access with javascript is document.cookie
but document.cookie
refers to the cookie that has been sent with the document (the page your script is running on) and won't get modified at any time. MDN states clearly it refers to the current document:
Document.cookie
Get and set the cookies associated with the current document. For a general library see this simple cookie framework.
Source : MDN
Any cookie set by an Ajax response does not belong to the current document.
How do I implement my csrf protection, then ?
The cookie to header token protection is the way to go. Note that the token you will send is the same during the whole session, and it is not suposed to change according to wild Ajax requests sending cookies.
Web applications that use JavaScript for the majority of their operations may use an anti-CSRF technique that relies on same-origin policy:
On login, the web application sets a cookie containing a random token that remains the same for the whole user session
Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/
JavaScript operating on the client side reads its value and copies it into a custom HTTP header sent with each transactional request
X-Csrf-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
The server validates presence and integrity of the token
Security of this technique is based on the assumption that only JavaScript running within the same origin will be able to read the cookie's value. JavaScript running from a rogue file or email will not be able to read it and copy into the custom header. Even though the csrf-token cookie will be automatically sent with the rogue request, the server will be still expecting a valid X-Csrf-Token header.
Source: Wikipedia : CSRF
With Angular 2+ this mission is fulfilled by the CookieXSRFStrategy
class.
Original answer
How and where do i access the x-csrf-token, and how do i add it to my requests?
Using CookieXSRFStrategy
seems to be the way to go to add it to your request. For the "how", unfortunately, the answer might be "you can't" (see further).
What does CookieXSRFStrategy('x-csrf-token', 'x-csrf-token'); exactly do. I don't like the blackbox feeling / understand the way the docs explained it.
CookieXSRFStrategy
/**
* `XSRFConfiguration` sets up Cross Site Request Forgery (XSRF) protection for the application
* using a cookie. See https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
* for more information on XSRF.
*
* Applications can configure custom cookie and header names by binding an instance of this class
* with different `cookieName` and `headerName` values. See the main HTTP documentation for more
* details.
*
* @experimental
*/
export class CookieXSRFStrategy implements XSRFStrategy {
constructor(
private _cookieName: string = 'XSRF-TOKEN', private _headerName: string = 'X-XSRF-TOKEN') {}
configureRequest(req: Request): void {
const xsrfToken = getDOM().getCookie(this._cookieName);
if (xsrfToken) {
req.headers.set(this._headerName, xsrfToken);
}
}
}
Source
Basically, it reads the cookie from document.cookie
and modify the Request
headers accordingly.
Right now i'm sending the cookie to the backend with the sessionid and the csrf token but what is sending it? The CookieXSRFStrategy or 'withCredentials' flag.
That's withCredentials
flag, this flag indicates that the XHR should send all the cookies that have been sent (even those previously set by Ajax response, but as cookies, not headers, and there is no way to change this behavior)
It doesn't set the header in my case ... but why ?
The cookie you are talking about is not sent with the document (index.html
) but from another ajax request. The fact is you cannot access cookies set by ajax response (see this answer), because that would be a security issue: a simple ajax get on www.stackoverflow.com
from a random web page would get the stack overflow cookie, and an attacker could steal it easily (if an Access-Control-Allow-Origin: *
header is present on stackoverflow response).
On the other hand, the document.cookie
API can only access to the cookies that are set when the document has been loaded, not any other.
So you should rethink the flow of your client/server communication on the server-side, because the only cookie that you will be able to copy to the headers is the one that has been sent with the document your script is running on (index.html).
it isn't a httpOnly cookie so it should be accessible with js even if it is X origin
httpOnly
makes the cookie unavailable to the document.cookie
API, but as I told you, document.cookie
refers to the cookie that has been sent with the document, not those sent via Ajax responses. You can make thousands of ajax call with a Set-Cookie
Header in the response, document.cookie
will still be the same string, without any modification.
Billion dollar solution
The server should only send one x-csrf-token
cookie containing the token with the document, and you should use that token that will be valid for the whole session for every request using CookieXSRFStrategy
. Why ? because that is how it works.