Home » ASP » ASP.MVC » Asp.Net MVC With the ValidateAntiForgeryToken For Cross Site Request Forgeries

Asp.Net MVC With the ValidateAntiForgeryToken For Cross Site Request Forgeries

Cross Site Request Forgeries is also know as CSRF. It is a type of malicious exploit that send commands from a user without his consent to another website. CSRF exploits the trust that a site has in a user’s browser. For example, a website could try to execute a form to add something in your Amazon basket! This can be limited by adding an hidden field to a form and a cookie. Both will contain the same value and when the form is submitted the hidden field is compared to the cookie value. If it’s the same value, we know that the user has sent the form. If it’s not, we know it’s a malicious attack.

In Asp.Net MVC, ValidateAntiForgeryToken is a combination of an html helper (hidden field) and attribute(cookie) that allows to verify if the form as been created and sent by the same user.

The first step is to add the HtmlHelper method @Html.AntiForgeryToken() inside the form in your view.

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    //...

This generate an hidden field with the name “__RequestVerificationToken” and an unique value. This is an example of the output generated:

<form action="/House/Create" method="post" novalidate="novalidate">
    <input name="__RequestVerificationToken" type="hidden" value="KPyzBk0KGpjStJR96AVI38AbBujBInJNB-1XH-RwsbuifwgmxnGiF-0R2cMHjcYBiz7yOBUnv0fZwoE2oBwiuKXalBDvsRr2RRG7nmkOsq41">
//...

The code behind the html helper is simple. It creates a MvcHtmlString from the AntiForgery class.

public MvcHtmlString AntiForgeryToken()
{
   return new MvcHtmlString(AntiForgery.GetHtml().ToString());
}

The AntiForgery class contains the GetHtml() used in the html helper but also some validation of the forgery. Both use the class AntiForgeryWorker.

public static HtmlString GetHtml()
{
  if (HttpContext.Current == null)
     throw new ArgumentException(WebPageResources.HttpContextUnavailable);
  else
     return AntiForgery._worker.GetFormInputElement((HttpContextBase) new HttpContextWrapper(HttpContext.Current)).ToHtmlString(TagRenderMode.SelfClosing);
}

The _worker that we see in the code above is of type AntiForgeryWorker. This class is about 139 lines and the core of it is the GetFormInputElement used by the GetHtml.

public TagBuilder GetFormInputElement(HttpContextBase httpContext)
{
    this.CheckSSLConfig(httpContext);
    AntiForgeryToken cookieTokenNoThrow = this.GetCookieTokenNoThrow(httpContext);
    AntiForgeryToken newCookieToken;
    AntiForgeryToken formToken;
    this.GetTokens(httpContext, cookieTokenNoThrow, out newCookieToken, out formToken);
    if (newCookieToken != null)
    this._tokenStore.SaveCookieToken(httpContext, newCookieToken);
    if (!this._config.SuppressXFrameOptionsHeader)
    httpContext.Response.AddHeader("X-Frame-Options", "SAMEORIGIN");
    TagBuilder tagBuilder = new TagBuilder("input");
    tagBuilder.Attributes["type"] = "hidden";
    tagBuilder.Attributes["name"] = this._config.FormFieldName;
    tagBuilder.Attributes["value"] = this._serializer.Serialize(formToken);
    return tagBuilder;
}

This is interesting to see how the value is generated. As we can see, it’s using the serializer to serialize the form token. The form token is build inside the class TokenValidator that take some property of the Identity. I won’t go deeper because it goes a little beyond the scope of the article. Just remember that the token is unique for the user. However, the GetFormInputElement code above is also important for something else. The token store role is to create the cookie which set also the token.

 public void SaveCookieToken(HttpContextBase httpContext, AntiForgeryToken token)
    {
      HttpCookie cookie = new HttpCookie(this._config.CookieName, this._serializer.Serialize(token))
      {
        HttpOnly = true
      };
      if (this._config.RequireSSL)
        cookie.Secure = true;
      httpContext.Response.Cookies.Set(cookie);
    }

The way the ValidateAntiForgeryToken attribute works is by checking to see that the cookie and hidden form field left by the Html.AntiForgeryToken() Html Helper essentially exists and match. If they do not exist or match, it throws an HttpAntiForgeryException.

ValidateAntiforgerytoken

The attribute does have a ValidateAntiForgeryTokenAttribute method that call the AntiForgery class. I have previously mentioned that it has not only the method to get the input but also for validation. Here is where it’s used.

public ValidateAntiForgeryTokenAttribute()
      : this(new Action(AntiForgery.Validate))
{
}

This method go through several method of the framework to end to the ValidateTokens(…) methods that look like the code below.

public void ValidateTokens(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken)
{
    if (sessionToken == null)
       throw HttpAntiForgeryException.CreateCookieMissingException(this._config.CookieName);
    if (fieldToken == null)
       throw HttpAntiForgeryException.CreateFormFieldMissingException(this._config.FormFieldName);
    if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken)
       throw HttpAntiForgeryException.CreateTokensSwappedException(this._config.CookieName, this._config.FormFieldName);
    if (!object.Equals((object) sessionToken.SecurityToken, (object) fieldToken.SecurityToken))
       throw HttpAntiForgeryException.CreateSecurityTokenMismatchException();
    string str = string.Empty;
    BinaryBlob binaryBlob = (BinaryBlob) null;
    if (identity != null && identity.IsAuthenticated)
    {
    binaryBlob = this._claimUidExtractor.ExtractClaimUid(identity);
    if (binaryBlob == null)
        str = identity.Name ?? string.Empty;
    }
    bool flag = str.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || str.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
    if (!string.Equals(fieldToken.Username, str, flag ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase))
        throw HttpAntiForgeryException.CreateUsernameMismatchException(fieldToken.Username, str);
    if (!object.Equals((object) fieldToken.ClaimUid, (object) binaryBlob))
        throw HttpAntiForgeryException.CreateClaimUidMismatchException();
    if (this._config.AdditionalDataProvider != null && !this._config.AdditionalDataProvider.ValidateAdditionalData(httpContext, fieldToken.AdditionalData))
        throw HttpAntiForgeryException.CreateAdditionalDataCheckFailedException();
}

What interest us is all possible exception that are throws. You can see that is can raise HttpAntiForgeryException for several case like if the cookie is missing, which is the case for Cross Site Request Forgeries or if the form doesn’t have the hidden field, or if the token mismatch between the cookie and the form.

All the complexity of the mechanism is hidden by the Asp.Net MVC framework. At the end, you only need to remember to add the html helper to your form and to add the attribute to the action of your controller that receive the form inputs.

If you like my article, think to buy my annual book, professionally edited by a proofreader. directly from me or on Amazon. I also wrote a TypeScript book called Holistic TypeScript

3 Responses so far.

  1. steve says:

    I get this when using MVC 4

    ‘System.Web.Mvc.ValidateAntiForgeryTokenAttribute.Salt’ is obsolete: ‘The ‘Salt’ property is deprecated. To specify custom data to be embedded within the token, use the static AntiForgeryConfig.AdditionalDataProvider property.’

    How does one implement the AntiForgeryConfig.AdditionalDataProvider with the ValidateAntiForgeryTokenAttribute? Does the attribute just go away and not use in the code any longer?

    I serach hundreds of website and each website pretty much just the same thing and defines it and talks about it. But I can not find any source code to download and see how it works? Do you have any insight.

    Below the wrapper ValidateAntiForgeryToken used in the code, how it was applied to a controller. Exactly what do I do the line of code where Salt = salt to prevent this error?

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class ValidateAntiForgeryTokenWrapperAttribute : FilterAttribute, IAuthorizationFilter
    {
    private readonly ValidateAntiForgeryTokenAttribute _validator;

    private readonly AcceptVerbsAttribute _verbs;

    public ValidateAntiForgeryTokenWrapperAttribute(HttpVerbs verbs) : this(verbs, null)
    {
    }

    public ValidateAntiForgeryTokenWrapperAttribute(HttpVerbs verbs, string salt)
    {
    this._verbs = new AcceptVerbsAttribute(verbs);
    this._validator = new ValidateAntiForgeryTokenAttribute()
    {
    Salt = salt
    };
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
    string httpMethodOverride = filterContext.HttpContext.Request.GetHttpMethodOverride();
    if (this._verbs.Verbs.Contains(httpMethodOverride, StringComparer.OrdinalIgnoreCase))
    {
    this._validator.OnAuthorization(filterContext);
    }
    }
    }

    [ValidateAntiForgeryTokenWrapper(HttpVerbs.Post, Constants.AntiForgeryTokenSalt)]
    public class AccountController : Controller

  2. steve says:

    For MVC4 the @Html.AntiForgeryToken you posted as also been depricated

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.