Search code examples
javascriptcookiescsrf

Double Submit Cookies and multiple tabs?


The double-submit cookie mechanism requires the use of cookies. However, cookies are shared across all browser tabs. How do you implement this mechanism without breaking the back button and browser tabs?

Meaning: if all tabs use the same cookie to store the CSRF token, every time a new tab is opened it would clobber the cookie value of all older tabs. When the forms in those older tabs are then submitted they will fail with a token mismatch.

On the other hand, if I use a separate cookie per tab how will the server know which cookie to look in? And also, if you need to use a hidden field to point to the cookie name, does it somehow open you up to new security attacks (since attackers that use CSRF can change form values)?

UPDATE:

  • The implementation needs to be stateless/RESTful. There is no server "session".
  • You can assume I control all sub-domains, so attackers won't be able to write their own cookies.

Solution

  • You can create a specific cookie that is generated once per user session. e.g. One named "CSRFCookie". Be sure to set the Secure Flag so this in only sent via HTTPS.

    As you are not maintaining server state be aware that this is vulnerable to MiTM attacks as I've covered here:

    Even if your site is only accessible over HTTPS and you correctly set the Secure Flag, care must be taken with this approach as an attacker could potentially MiTM any connection from the victim to any HTTP website (if the attacker is suitably placed of course), redirect them to your domain over HTTP, which is also MiTM'd and then set the required cookie value. This would be a Session Fixation attack. To guard against this you could output the cookie value to the header and the hidden form field every time this page is loaded (over HTTPS) rather than reuse any already set cookie value. This is because although a browser can set the Secure Flag, it will still send cookies without the Secure Flag over a HTTPS connection, and the server will not be able to tell whether the Secure Flag was set. (Cookie attributes such as the Secure Flag are only visible when the cookie is set, not when it is read. The only thing the server gets to see is the cookie name and value.)

    So if you wish to protect against this and not break your site over multiple tabs, you must keep track of the "CSRFCookie" value server side.

    Otherwise, if server state is not stored:

    1. For a secure approach against MiTM, this value must be generated on each form load but breaks tabs as the cookie value is changed each time.
    2. If you do not wish to protect against MiTM you could just generate the "CSRFCookie" value once, which will allow multiple tabs/back to work.

    There may be a solution for (1), which involves checking whether a cookie value exists, and if it already does you increment a number at the end of the name of the cookie value and hidden form field. e.g. "CSRFCookie1", "CSRFCookie2", etc, so each form can have their own cookie. Be aware though that this may build up a huge amount of data that will be submitted with every HTTP request by the browser to your server (even images, .js etc).

    Another option is to use HSTS to ensure the browser always redirects any HTTP connection to your server to HTTPS before any request is made. However, there is still a window of opportunity for the HTTP connection to be MiTM'd before any request is made to your server and the HSTS policy set. You could however arrange for your site to be in the HSTS pre-loaded list of the most popular browsers to reduce the risk. This will enable you to use multiple tabs without server state as you simply use the same cookie ("CSRFCookie") and value on every form that is generated if not set or read if already set then never regenerated. Please note that HSTS is not yet supported in IE, but support may be added in IE 12, however see here for Edge browser support.