Thursday, October 4, 2018

Double Submit Cookies Pattern

As I mentioned in my previous posts, Double Submit Cookies Pattern is another mechanism of preventing CSRF attacks.

Similar to the Synchronized Token Pattern, a random string token is generated whenever a user logs in and a session starts in Double Submit Cookies as well. One of the main differences is that the server only generates the token, and does not store it in the database in Double Submit Cookies Pattern, whereas the token is stored in the database in Synchronized Token Pattern.

What happens here is after generating the CSRF token, the server sets a cookie which contains the CSRF token in the client's browser. 

If we take the same example as in the Synchronized Token Pattern, where an admin fills a form to create a user account, in a similar fashion, in Double Submit Cookies Pattern too, a hidden field which contains the CSRF token must be embedded in the form. The CSRF token value to be embedded is not retrieved from the server side through an AJAX call like in Synchronized Token Pattern. In Double Submit Cookies Pattern, we retrieve the CSRF token value from the CSRF cookie stored in the browser using Javascript.

To read cookies through Javascript, the domain should match with the current domain which means only the cookies of the current domain will be visible. CSRF cookie cannot be protected by httpOnly flag that is used to prevent any client side Javascripts from reading and accessing this cookie because the requirement of the problem needs the reading of the CSRF token using Javascript.

When the request is sent to the server, either the body will contain the CSRF token embedded in the form or the header, and since the browser selects and sends all the cookies that fall under the current domain and path to the server along with all the requests to maintain the session, the server can easily validate whether the current session is valid or not by reading the received CSRF token cookie value and comparing it against the received CSRF form-embedded token value.

Therefore, this makes it impossible for attackers to obtain and access the cookies for another domain, and attackers' request body/header will not have this token.

The diagram below illustrates the flow of the Double Submit Cookies Pattern process mentioned above:



We do not protect GET requests against CSRF attacks, as we expect client to send the CSRF token, and the token cannot be sent in the query parameters of a GET request as it exposes the token.

You can visit this GitHub link to view the sample implementation of Double Submit Cookies Pattern to prevent CSRF. I have hard-coded the username and password for demonstration purpose.

Username and Password both: ssd

As in the diagram above:


   1. User logs in to the system using index.html. The form action points to login.php, and the form details will be submitted to it when the user clicks the Login button.

   Login Page:

   index.html:







   
   
   2. If the user has provided the correct login credentials, the user is redirected to add_user.html.

   login.php:
























As in Synchronized Token Pattern, for each login, a new session is made for the user, and it is ensured that a new PHP Session Id is generated for each login after invalidating the already existing session.


Similar to the approach taken in Synchronized Token Pattern, the CSRF Token, being a randomly generated string, in this case, has been produced by hashing and Base64 encoding the random integer created while taking the current timestamp into account, through the line:
$csrfToken = base64_encode(hash("sha256", uniqid(mt_rand(1000, 9999)
.microtime(), true), true));

Afterwards, setcookie() function in PHP has been used to create and store a cookie which contains the generated CSRF Token value in the user's browser, through the line:
setcookie($cookie_name, $cookie_value, time() + (86400 * 30), "/");
Cookies normally contain the five key-value pairs: Cookie Name, Cookie Value, Domain, Path, and Expiry Date. As it can be seen the Name of the Cookie is set to be "CSRF_TOKEN", Value is the generated CSRF Token value, and Path is "/" (root) which means all the paths of the particular domain to which the cookie belongs. The Cookie is set to expire after 30 days. The domain is not mandatory to be added to the setcookie() function along with the other required and appropriately added details as the browser automatically sets it as the current domain as it is the domain setting the cookie in the browser at that point of time.  

   3. The CSRF Token for the session is retrieved from the CSRF_TOKEN cookie stored in the user's browser using JavaScript, and when the user fills the form and clicks the Insert button, the form details are submitted to the pointed validate_token.php.

   Add User Page:

   add_user.html:


























When the form has completely loaded as checked by the document.readyState if statement, the CSRF token value is retrieved from the cookie stored in the user's browser by calling getCSRFToken() method, passing the Cookie Name as a parameter, and after retrieving the CSRF token value, a new hidden input field is added to the form, and this input field contains the retrieved CSRF token which is to be passed to the server-side in the attempt in enabling the server to recognize whether the request is legitimate or not.

getCSRFToken() method works by accessing cookies of the current domain using document.cookie, decoding the retrieved value which contains all the cookies for the current domain, and splitting by the delimiter ";" to separate key-value pair cookies of the retrieved cookie set. Then, in a loop, each key-value pair is accessed and the white spaces in the beginning are removed, if found, and afterwards, if it is possible to find the correct key-value pair which contains the string: "CSRF_TOKEN=" and the CSRF Token value, the CSRF Token value which is the value in the key-value pair is extracted and returned. 

   4. When the form details are submitted to validate_token.php on Insert button click, the CSRF Token values will be compared and validated, and a message will be returned based on the outcome of the comparison.

   validate_token.php:































validate_token.php checks whether the request is POST and checks to see whether the PHPSESSID which is the PHP Session Id has been set. Additionally, it checks to see whether a value has been set as the 'csrf_token' in the request body and also in the CSRF_TOKEN cookie, and if it has been set, the CSRF Token value stored in the received CSRF Token Cookie is compared with the value sent in the POST request body to see whether the values are equal and the request is legitimate. Either a successful message as in the picture included below, or an error message will be displayed in the client-side based on the equality comparison outcome.

This way we do not need to store a whole set of CSRF Tokens mapped against each user's each session in the database.

No comments:

Post a Comment