Search code examples
javascriptsecuritycsrfwebsecurity

Is it unsafe for the javascript client to set the CSRF token?


It seems to me that the primary goal of CSRF is to confirm that the client making the request is the client we expect.

The solution I've commonly seen is:

  1. Server generates random CSRF Token
  2. Server sets CSRF token in cookie
    1. Server injects the CSRF token into the form when generating the form OR
    2. Server passes the CSRF token to javascript and javascript injects the CSRF token as a header on XMLHTTPRequests
  3. When a request is received, it's validated by checking that the CSRF token in the cookie matches the CSRF token in header/form value.

It makes a lot of sense to me that the server is generating the CSRF for (3)(1), but I cannot come up with a reason why it's necessary for (3)(2).

Instead, if the client is pure javascript, I believe this is safe:

  1. Javascript generates a random CSRF token
  2. Javascript sets the CSRF token in a cookie
  3. Javascript passes the CSRF token in the header when making an XMLHTTPRequest
  4. Server checks that CSRF token in the header and cookie match

My understanding is that 3 and 4 are both things an attacker cannot do, so this would also sufficiently block attacks. Is that correct?

If that is safe, do we even need to do step (1) and (2)? Would this be safe as well because of same-origin policy (assuming cors is configured properly)?

  1. Javascript sets an 'CSRF-Safe: true' header in XMLHTTPRequest
  2. Server checks that the header CSRF-Safe exists and is set to "true"

Solution

  • Yes, both of those simplified approaches should be safe in the presence of CORS and the same-origin policy. In fact, you don't even need the CSRF-Safe: true header as long as you validate the content-type.

    Wikipedia confirms:

    If data is sent in any other format (JSON, XML) a standard method is to issue a POST request using XMLHttpRequest with CSRF attacks prevented by SOP and CORS; there is a technique to send arbitrary content from a simple HTML form using ENCTYPE attribute; such a fake request can be distinguished from legitimate ones by text/plain content type, but if this is not enforced on the server, CSRF can be executed[12][13]