You need the token to be sent in the request outside of the cookie mechanism (e.g. POST data). If sessions are managed via a session ID contained within a cookie, your approach doesn't prevent CSRF.
In your current implementation:
- save token to session;
- save token to client's cookie via Set-Cookie header.
This will have the effect of the token being saved in two locations, but both locations will be identified by cookie values.
e.g. if the token is "
12345678" it will be saved in session (pseudocode):
Session["SessionCSRFToken"] = "12345678";
and in cookies
However, in order to save the value in session, the web application framework will create a cookie to identify the session, so you will get two cookies set:
If a user is logged into your website at
www.foo.com and then the user visits
www.evil.com, there could be a hidden
form tag embedded into the page of
<form method="post" action="https://www.foo.com/Account/DeleteAccount">
When the browser automatically submits the form, both cookies will be sent with the request
SessionID=987654321) and your cookie token (
CSRFToken) and session token (
SessionCSRFToken) will match and the effect will be that the user's account is closed on your system without their knowledge.
To prevent this, you need to store a CSRF token in the page itself (e.g. within a hidden input). This way your form could only be submitted as follows:
<form method="post" action="/Account/DeleteAccount">
<input type="hidden" name="CSRFToken" value="12345678" />
www.evil.com has no way of including the hidden input within their own HTML as they have no way of knowing the value of the
CSRFToken. The value of
CSRFToken can be the same as the cookie (the session value is now no longer needed), but it only needs to be generated once pre user session (there is no need to constantly regenerate the value).
POST you simply need to make sure that
Request.Form["CSRFToken"] == Request.Cookies("CSRFToken").