Protection against Cross-site request forgery (CSRF, XSRF)
Cross-site request forgery (CSRF) is one of the most common web applications vulnerabilities. In 2013 it was ranked number 8 in OWASP’s TOP 10 document. In this article I am going to explain what is the CSRF vulnerability and the available protection methods.
What is the CSRF?
Cross Site Request Forgery (CSRF) allows an attacker to perform unauthorized activities without the knowledge of a user. Here’s how OWASP summarizes it - CSRF is malicious in the sense that it inherits the identity and privileges of the victim to perform an undesired function on the victim's behalf.
Browser requests automatically include any credentials associated with the site, such as the user's session cookie, Windows domain credentials, etc. A CSRF attack is successfully executed if the following conditions are met:
- user is authenticated to the site,
- user clicks on malicious link, button or any malicious HTML element.
Let me illustrate the CSRF concept in the following schema:
- The attacker uses one of the phishing methods, for example, he sends an email containing a malicious link to the victim.
- The victim clicks on the malicious link and is redirected to the attacker’s website. Now the attacker needs to trick the victim to click a malicious element placed on the page.
- In the following example, when the victim clicks on the submit button “Donate to charity”, a regular POST request will be submitted:
<form action="http://yourwebsite.com/transfermoney.aspx" method="POST">
<input type="hidden" name="account" value="Attacker"/>
<input type="hidden" name="amount" value="9999"/>
<input type="submit" value="Donate to charity"/>
- The POST request is sent to the server with victim’s credentials (authentication cookie). The web server processes the request and sends $9999 to the attacker’s account. The most poignant fact here is that you as the victim are not aware of any suspicious behavior.
Protection using ViewState
ViewState can be used as a defense mechanism against CSRF, because it is more difficult for an attacker to forge a valid ViewState. Let me touch briefly on the main purpose of ViewState in ASP.NET:
- ViewState maintains the state of pages between postbacks.
- ViewState is stored in a hidden field on the page.
- ViewState is encoded using Base64 encoding.
Keep in mind that ViewState without any configuration is not protected against any security attacks since it can be simply decoded and the data contained can be manipulated.
Example of unprotected ViewState:
To protect ViewState against CSRF, you need to allow Viewstate MAC encoding. The main idea is that the framework uses the MAC address of the computer to create a hash and appends it to the encoded ViewState. Now, if an attacker wants to manipulate ViewState data, he will need to know the unique key used to create the hash. Otherwise, ViewState validation will fail and the attack will not be successful.
In a real scenario, the attacker would access the page, change the values to the expected output (e.g. change user e-mail) and rendered the valid ViewState. Such a ViewState will be forged to the victim and the server will accept the request because it contains a valid data. That brings us to the problem - ViewState hash is computed the same way for all users who visit the page – user can pre-generate valid ViewState with hijacked values.
Even if we enabled ViewState validation, the website is still vulnerable to CSRF attack. It requires a bit more orchestration to build a safe ViewState string.
Example of protected ViewState (red part is the hash that protects the ViewState):
Remember that MAC encoding is the first step you have to do to protect your website against CSRF. Let’s improve our protection and make the hash user-specific. If the ViewState hash is different for each user, nobody will be able to predict its value.
To complete the ViewState protection, you have to set the ViewStateUserKey property. I recommend setting its value to the user’s session ID, which is unique for each user. In combination with the server’s MAC address, the ViewStateUserKey value is used to create a unique hash and avoid a CSRF attack. ViewStateUserKey in Kentico is configured by default for all pages.
Example of user-specific ViewState (the red part is the user-specific hash, different for each user):
Let’s have a look at the scenario mentioned above again. If an attacker tries to access the page, change form values, and render output, the ViewState hash will be created using the attacker’s session ID. Such a faked request throws a validation exception when the victim tries to execute it. The victim and the attacker will have different session IDs, therefore ViewState validation will fail. Your web site is now protected against CSRF.
Feel free to read the documentation related to CSRF protection in Kentico.
Protection using tokens
The next method I’m going to talk about is called stateless CSRF defense, or the double submit method. The main idea is that the client sends two tokens back to the server. The first token is sent in the POST request as a hidden field, the second token is sent in a cookie.
When the user visits the website (1), an anti-CSRF cookie is created (2) on the first GET request. The anti-CSRF cookie expires with the session, which ensures a unique anti-CSRF cookie for every user. If the page has any forms on it, the hidden field containing the protected anti-CSRF cookie value is rendered on that page.
Now, if a user submits a POST request (3), e.g., by clicking a button on the page, the anti-CSRF protection is forced. The server validates if the value from the cookie and the value from the hidden field match. The request is accepted only if these values match (4). Otherwise, the server refuses the request as a potential CSRF attack.
Kentico CSRF token protection (new in Kentico 9)
Kentico CSRF protection will be based on anti-CSRF cookies and anti-CSRF form hidden fields.
Anti-CSRF Cookie value will be a cryptographically random number generated by the RNGCryptoServiceProvider class converted to a Base64 string. The Anti-CSRF cookie will be a session cookie and it will be marked as HttpOnly.
Anti-CSRF Hidden field value will be the cryptographically random generated number from the cookie, protected by the machine key method MachineKey.Encode. The method encrypts the data and appends a hash-based message authentication code.
Anti-CSRF token mechanism
The anti-CSRF protection mechanism is described in the picture above. On the first GET request to the server, the unique Base64-encoded anti-CSRF cookie is created. The anti-CSRF token stored in the hidden field is computed based on this cookie value. All pages containing the form HTML element render the anti-CSRF hidden field.
If a user invokes a POST request, the anti-CSRF cookie is appended to the request. The web server determines if the request is valid or not. The cookie value is base64 decoded, the hidden field value is decoded using MachineKey.Decode method and these values are matched. Based on the result the request is accepted or refused. Since the attacker cannot access the victim’s anti-CSRF cookie, he cannot compute the anti-CSRF token and thus prepare a valid POST request. This approach is illustrated on the following pseudo code:
if (Request is POST)
cookieValue = get base64 decoded cookie value
hiddenFieldValue = get machinekey decoded hidden field value
if (cookieValue is null or empty) or (HiddenFieldValue is null or empty)
throw CSRF exception
if (cookieValue != hiddenFieldValue)
throw CSRF exception
Which method should I use?
ViewState protection is useful when these requirements are met:
- ViewState is enabled.
- ViewState validation is enabled.
- ViewStateUserKey is set to a unique value (per user).
- All pages inherit from CMSPage or AbstractCMSPage.
Be aware that:
- Performance can decrease.
- It is useless on pages where ViewState is disabled.
- It works only for ASP.NET WebForms.
Token protection is easy to use because:
- It protects all pages (regardless of ViewState settings).
- It doesn’t influence performance.
- You don’t need to inherit from CMSPage.
Keep in mind both methods are designed to protect only POST requests. You should never perform a sensitive action on GET requests (e.g. delete accounts, change passwords, etc.). The HTTP specifications state that GET requests should be idempotent (i.e., they should not cause state changes within your application). If you have URLs which change the state of your application on GET request, you should consider changing the type of your requests to POST.
Kentico takes Cross-site request forgery vulnerability really seriously. Therefore, we will improve the ViewState protection mechanisms already available since Kentico 7 and implement a new CSRF protection – anti-CSRF tokens – in Kentico 9.
Finally, let me briefly mention the most important facts about CSRF:
- CSRF is a serious security issue.
- Kentico protects your web sites using ViewState by default.
- Kentico 9 will improve the protection against CSRF by employing anti-CSRF tokens.
- Both methods have pros and cons – it depends on project specifics.
I hope this will help you understand how CSRF works and how you can protect your web sites against CSRF. Let me know what you think in the comments!