There are 3 kinds of attributes for cookies.
Session / Persistent Cookie
These are the normal cookies. You can either let cookie cleared when the browser is closed (called “session cookie” since it only sticks around for the duration of user session), or if you can persist them by setting “expires” attribute which makes it a persistent cookie.
document.cookie = "name=value; expires=Fri, 31 Dec 9999 23:59:59 GMT;";
Browser will only transmit the cookie to server if the request is using HTTPS. You can set it from server by setting following in the response header.
Set-Cookie: access_token=secretpanda; secure;
Set-Cookie: access_token=secretpanda; HttpOnly;
If you are using cookie for authentication, or using to transmit sensitive information, Secure or HttpOnly cookies alone won’t cut it.
A bad site could hide (with or without the site owner’s intention) XHR requests to force your browser into to issuing requests on user’s bank website (if he/she happens to be authenticated) to send money to attacker’s account. The bank website will think that it’s the user making such requests and execute them - especially if the bad site is trusted in bank site’s CORS (maybe they are under the same domain name?)
Recommended mitigation is to do following
(Stateful) CSRF Token
For server side session enabled site, you can do following..
- First of all, make sure there is no possibility of XSS (otherwise rest of the mitigations are pointless)
- Generate a random token (called CSRF token) that is associated with the user’s current session on the server side.
- Send this token on each page and make it submitted back to the server as part of form parameters.
- When a server receives this token, make sure that it matches the original token found in the session for that user.
(Stateless) CSRF Token *double submit*
Or, if you want to make this mitigation state-less (does not require a session), you can use the double-submit method.
- (Again) First of all, make sure there is no possibility of XSS (otherwise it’s pointless)
- Generate a random token and send it to the browser via the cookie.
- Send the same token on each page and make it submitted back to the server as part of form parameters (thus called double-submit)
- When a server receives a request, make sure that the token from the cookie matches the token in the form parameter. Since the bad site can’t read the value of CSRF token (due to same-origin policy), bad site doesn’t know what value to set for CSRF parameter on the submitted form.
The “gotchas” on above CSRF mitigations lays on the first step; make sure there is no possibility of XSS… You could make sure to escape all of your text output, but that’s just the beginning. If you are using any external libraries written by someone else, how confident are you that none of those libraries you depends on does not include any bug that could allow XSS attacks?
Since there is no way to be 100% fool proof with cookies anyway, I often use localstorage to store access token (JWT) on my apps. I have more control over localstorage like when to send what information to server, and I can store much bigger amount of data and longer period of time than cookie.