Note: This guide describes Kentico CMS version 7. Unfortunately, we cannot support this guide from version 8 forward. Go to latest documentation

Skip to end of metadata
Go to start of metadata

A browser typically uses two ways of requesting web applications. It sends data via URL parameters where HTTP GET request is used and sends data via forms where HTTP POST is used. The application typically does some action, for example, inserts a new user into a table, deletes a forum post, etc. And here is a problem. The web application typically does not check if the requests are generated by the web application itself (user clicks a link or sends a filled form). An attacker can create a link for some action and send it to the user. The user then clicks the link and the action is performed without the user even noticing. This is called Cross Site Request Forgery.

So a user has to click an attacker's link or fill an attacker's form. Another condition is that the user must be logged on to a vulnerable web. These days, almost every application provides the "keep me logged in" functionality, so this condition is easily met.

ASP.NET complicates a successful attack because of ViewState. If ViewState is turned on, you cannot send tampered POST requests to an ASP.NET application because validation of ViewState fails. For this reason, many developers think that an ASP.NET application is bulletproof against CSRF. However, this is not exactly true:

  • GET requests can still cause CSRF.
  • ViewState can be generated outside an application if you do not use machine keys as keys for ViewState encoding.
  • Even if you use machine keys to encrypt your ViewState, it is not 100% safe. ASP.NET does not take form values from Request.Form but from Request.Params. This is the reason why it is possible to perform a so-called One click attack. It is a special case of CSRF. An attacker simply sends ViewState and values of form fields via GET. The trick is that the attacker can use ViewState generated by ASP.NET after POST and change values of fields and the validation will still succeed.

 

On this page

Example of CSRF

Let's have a simple page without any content and this code in code behind:

if (!string.IsNullOrEmpty(Request.Form["UserID"]))
{
	DoSomeAction(Request.Form["UserID"]) ;
} 

It does not matter what the DoSomeAction() method does. The important thing is that, in this case (if a machine key is not used to encode ViewState), the action is performed with the UserID specified in the UserID field. Anyone who is authorized and sends a form with this field can perform the action. And there is no way to check if the actual user really wants to do this action.

Let's have another page without content with this code behind:

if (CMSContext.CurrentUser.IsGlobalAdministrator)
{
	int userID = QueryHelper.GetInteger("UserID", 0);
	if (userID != 0)
	{
		Response.Write("I've just deleted a user with id: " + userID);
	}
}
else
{
	Response.Write("You don't have enough permissions to delete the user");
} 

This code is similar to the previous example. The only difference is that now, UserID is taken from a query string (by the GET method).

The third example shows a one-click attack. Let's have a simple page with a textbox and a button. This code handles the Onclick action of the button:

protected void btnSend_Click(object sender, EventArgs e)
{
	Response.Write(txtUserID.Text);
} 

Users typically insert a value into the txtUserID textbox and click the button. But the attacker can forge a link to the user:

http://site/Page.aspx?_VIEWSTATE=/wEPDwULLTE0MDM4MzYxMjNkZIb5PxpCoDI4Dt3C2LKzz8CnHkbd&txtUserID=<anything>&btnSend=Send&_EVENTVALIDATION=/wEWAwLdr4fPBgLT8dy8BQKFzrr8AbhBL27NfMMamif/pHIFUlo41HNI

The attacker can change the <anything> macro to anything else. ViewState is taken from the page that can be generated after postback on that page and the validation is successful.

What can CSRF attack do

It depends on the application and on the security of the web server. For example, if the application is poorly implemented and encoding of ViewState based on machine key is turned off on the server, then the attacker can do anything that the victim of the attack could normally do.

Finding CSRF vulnerabilities in Kentico CMS

If you find any page/control/etc., that does an action on GET request, there is a possibility of a CSRF vulnerability. For example, try to find the following and similar strings in your source code:

  • QueryHelper.GetString("action")
  • EnableViewState="false"
  • EnableViewStateMac="false"

If you find any of these strings in the <%@ page directive, it means that a developer turned off ViewState validation (first case) or machine keys for ViewState encoding (second and third case).

The ViewState validation helps a lot to avoid POST CSRF, so globally, it must always be turned on. Also, if you find any page that does not inherit from a Kentico class, it means that there is a possibility of CSRF.

Of course, if a page doesn't have these features and the page does not do any actions, it also does not have to be protected from CSRF.

Avoiding CSRF

Even if your application encodes ViewState properly, a special case of CSRF, a one click attack, is still possible. The problem is simple – ViewState is the same for all users. However, you can specify a key corresponding to the current user by the ViewStateUserKey page property. The recommended value is a user's session ID. All CMS pages must inherit from CMSAbstractPage (the base page for all CMS pages). CMSAbstractPage page sets ViewStateUserKey to a unique value for every user and thus avoids one click attacks.

Because ASP.NET partially secures web applications from POST CSRF and we add the least needed functionality to protect all POST requests by default, use POST requests only for actions. To enable machine key encoding or ViewState validation, you do not need to perform any actions.

By default (on the level of the global web.config file), ASP.NET is set to:

<machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="SHA1" decryption="Auto" compatibilityMode="Framework20SP1" />
<pages buffer="true" enableSessionState="true" enableViewState="true" enableViewStateMac="true" viewStateEncryptionMode="Auto">

It means that machineKey is generated automatically, ViewState is validated and encoding of ViewState based on machineKeys is also enabled. If a control requests it, ViewState is encrypted by SHA1.

Summary

  • Do not use GET requests to perform actions, always use POST.
  • Never turn off validation of ViewState on a page (key EnableViewState) globally.
  • Never turn off encoding of ViewState based on machineKey (EnableViewStateMac) on a page globally.
  • Do not set the CMSUseViewStateUserKey key to false (it is an internal key which can cause insertion of the current user's key to ViewState encoding).
  • If you insert a new page, always make it inherit from some of the CMS pages.
  • If you create a new CMS page class, check that your page directly or indirectly inherits from CMSAbstractPage.