39

I have created an ASP.Net Web Forms application using Visual Studio 2013 and I am using .NET Framework 4.5. I want to make sure my site is secure from Cross-Site Request Forgery (CSRF), I have found many articles talking about how this feature is implemented on MVC apps, but very few talking about Web Forms. On this StackOverflow question one comment states that

"This is an old question, but the latest Visual Studio 2012 ASP.NET template for web forms includes anti-CSRF code baked into the master page. If you don't have the templates, here's the code it generates:..."

My master page does not contain the code mentioned in that answer. Is it really included in new applications? If not, what is the best way to add it?

Chris
  • 5,876
  • 3
  • 43
  • 69
Nada N. Hantouli
  • 1,310
  • 1
  • 12
  • 20

4 Answers4

51

You could try the following. In the Web-Form add:

<%= System.Web.Helpers.AntiForgery.GetHtml() %>

This will add a hidden field and a cookie. So if you fill out some form data and post it back to the server you need a simple check:

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
        AntiForgery.Validate(); // throws an exception if anti XSFR check fails.
}

AntiForgery.Validate(); throws an exception if anti XSFR check fails.

Nerdroid
  • 13,398
  • 5
  • 58
  • 69
Saftpresse99
  • 849
  • 7
  • 16
  • 12
    The namespace is System.Web.Helpers in the Microsoft.AspNet.WebPages Nuget-Package. – Saftpresse99 Aug 16 '16 at 05:30
  • 2
    Anyone know if there's a package available that doesn't depend on Razor? – ryanulit Oct 09 '18 at 22:00
  • 1
    How about `ashx` handlers ? is this efficient, I have a lots of `ajax` in `HTML` pages that `System.Web.Helpers.AntiForgery.GetHtml()` can not be added to `HTML` page. – Aria Mar 10 '19 at 06:25
  • I saw here [link](https://github.com/DevExpress/aspnet-security-bestpractices/tree/master/SecurityBestPractices.WebForms#4-preventing-cross-site-request-forgery-csrf) that that this markup `` also has to be added. Is this necessary? – Dov Miller Jul 25 '21 at 12:44
  • Does this also validate ajax calls such as the ones done by using XMLHttpRequest or the ones done by asp.net itself such as ScriptResource.axd? If it does not, then this would not be a viable solution for CSRF attacks. – Robert Smith Aug 11 '21 at 15:25
37

ViewStateUserKey & Double Submit Cookie

Starting with Visual Studio 2012, Microsoft added built-in CSRF protection to new web forms application projects. To utilize this code, add a new ASP .NET Web Forms Application to your solution and view the Site.Master code behind page. This solution will apply CSRF protection to all content pages that inherit from the Site.Master page.

The following requirements must be met for this solution to work:

All web forms making data modifications must use the Site.Master page. All requests making data modifications must use the ViewState. The web site must be free from all Cross-Site Scripting (XSS) vulnerabilities. See how to fix Cross-Site Scripting (XSS) using Microsoft .Net Web Protection Library for details.

public partial class SiteMaster : MasterPage
{
  private const string AntiXsrfTokenKey = "__AntiXsrfToken";
  private const string AntiXsrfUserNameKey = "__AntiXsrfUserName";
  private string _antiXsrfTokenValue;

  protected void Page_Init(object sender, EventArgs e)
  {
    //First, check for the existence of the Anti-XSS cookie
    var requestCookie = Request.Cookies[AntiXsrfTokenKey];
    Guid requestCookieGuidValue;

    //If the CSRF cookie is found, parse the token from the cookie.
    //Then, set the global page variable and view state user
    //key. The global variable will be used to validate that it matches 
    //in the view state form field in the Page.PreLoad method.
    if (requestCookie != null
        && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue))
    {
      //Set the global token variable so the cookie value can be
      //validated against the value in the view state form field in
      //the Page.PreLoad method.
      _antiXsrfTokenValue = requestCookie.Value;

      //Set the view state user key, which will be validated by the
      //framework during each request
      Page.ViewStateUserKey = _antiXsrfTokenValue;
    }
    //If the CSRF cookie is not found, then this is a new session.
    else
    {
      //Generate a new Anti-XSRF token
      _antiXsrfTokenValue = Guid.NewGuid().ToString("N");

      //Set the view state user key, which will be validated by the
      //framework during each request
      Page.ViewStateUserKey = _antiXsrfTokenValue;

      //Create the non-persistent CSRF cookie
      var responseCookie = new HttpCookie(AntiXsrfTokenKey)
      {
        //Set the HttpOnly property to prevent the cookie from
        //being accessed by client side script
        HttpOnly = true,

        //Add the Anti-XSRF token to the cookie value
        Value = _antiXsrfTokenValue
      };

      //If we are using SSL, the cookie should be set to secure to
      //prevent it from being sent over HTTP connections
      if (FormsAuthentication.RequireSSL &&
          Request.IsSecureConnection)
      {
        responseCookie.Secure = true;
      }

      //Add the CSRF cookie to the response
      Response.Cookies.Set(responseCookie);
    }

    Page.PreLoad += master_Page_PreLoad;
  }

  protected void master_Page_PreLoad(object sender, EventArgs e)
  {
    //During the initial page load, add the Anti-XSRF token and user
    //name to the ViewState
    if (!IsPostBack)
    {
      //Set Anti-XSRF token
      ViewState[AntiXsrfTokenKey] = Page.ViewStateUserKey;

      //If a user name is assigned, set the user name
      ViewState[AntiXsrfUserNameKey] =
             Context.User.Identity.Name ?? String.Empty;
    }
    //During all subsequent post backs to the page, the token value from
    //the cookie should be validated against the token in the view state
    //form field. Additionally user name should be compared to the
    //authenticated users name
    else
    {
      //Validate the Anti-XSRF token
      if ((string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue
          || (string)ViewState[AntiXsrfUserNameKey] !=
               (Context.User.Identity.Name ?? String.Empty))
      {
        throw new InvalidOperationException("Validation of " +
                            "Anti-XSRF token failed.");
      }
    }
  }
}

Source

Synctrex
  • 811
  • 1
  • 11
  • 19
Nada N. Hantouli
  • 1,310
  • 1
  • 12
  • 20
  • 3
    I'm using VS2017 and I don't see this code in Site.Master.cs (brand new web forms application project). – joym8 Oct 02 '17 at 22:09
  • 3
    We used this code for about a year and are constantly experiencing a problem: exception is thrown sporadically. We have our custom authentication module keeping an auth (long lived) cookie. When user opens up a browser and navigates to the site, it will log him in automatically based on that cookie. Once I was lucky enough to at least hit breakpoint and see which condition failed: it was the second term. Context.User.Identity.Name was empty, but the ViewState contained user name. Why do we even stuff and check the user name? – Nickolodeon Jun 26 '18 at 15:08
  • Maybe I'm missing something, but it seems an attacker can easily get around this code by just viewing the source of the current page and copying the "__VIEWSTATE" and "__VIEWSTATEGENERATOR" hidden inputs, then resubmitting. I was actually able to do it successfully myself. How do you protect against that? – ryanulit Oct 10 '18 at 18:23
  • 2
    @ryanulit the idea behind XSRF is that someone can make a request pretending to be the client from a completely different domain (via an or – Chris Dec 17 '18 at 17:10
  • Does anyone know what would be the equivalent of this code in vb.net ? I had issues with the event lines. – love2code Sep 30 '19 at 15:35
  • @joym8 you have to include an Authentication option. – cdonner Feb 05 '20 at 15:17
  • does it really matter that AntiXsrfTokenKey variable to set exactly as "__AntiXsrfToken" – Alireza Masali May 26 '20 at 10:13
  • @Nickolodeon Where you able to work around the authentication issue? The code generated by the templates doesn't handle the use case where user auth cookie expires and they postback to the same page (which can be viewed by both, anonymous and authenticated users). – ActiveX Jul 08 '20 at 17:19
18

When you create a new 'Web Form Application' project in VS 2013, the site.master.cs will automatically include the XSRF/CSRF code in the Page_Init section of the class. If you still dont get the generated code, you can manually Copy + Paste the code. If you are using C#, then use the below:-

private const string AntiXsrfTokenKey = "__AntiXsrfToken";
private const string AntiXsrfUserNameKey = "__AntiXsrfUserName";
private string _antiXsrfTokenValue;

 protected void Page_Init(object sender, EventArgs e)
    {
        // The code below helps to protect against XSRF attacks
        var requestCookie = Request.Cookies[AntiXsrfTokenKey];
        Guid requestCookieGuidValue;
        if (requestCookie != null && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue))
        {
            // Use the Anti-XSRF token from the cookie
            _antiXsrfTokenValue = requestCookie.Value;
            Page.ViewStateUserKey = _antiXsrfTokenValue;
        }
        else
        {
            // Generate a new Anti-XSRF token and save to the cookie
            _antiXsrfTokenValue = Guid.NewGuid().ToString("N");
            Page.ViewStateUserKey = _antiXsrfTokenValue;

            var responseCookie = new HttpCookie(AntiXsrfTokenKey)
            {
                HttpOnly = true,
                Value = _antiXsrfTokenValue
            };
            if (FormsAuthentication.RequireSSL && Request.IsSecureConnection)
            {
                responseCookie.Secure = true;
            }
            Response.Cookies.Set(responseCookie);
        }

        Page.PreLoad += master_Page_PreLoad;
    }

    protected void master_Page_PreLoad(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // Set Anti-XSRF token
            ViewState[AntiXsrfTokenKey] = Page.ViewStateUserKey;
            ViewState[AntiXsrfUserNameKey] = Context.User.Identity.Name ?? String.Empty;
        }
        else
        {
            // Validate the Anti-XSRF token
            if ((string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue
                || (string)ViewState[AntiXsrfUserNameKey] != (Context.User.Identity.Name ?? String.Empty))
            {
                throw new InvalidOperationException("Validation of Anti-XSRF token failed.");
            }
        }
    }
Mahesh Kava
  • 773
  • 5
  • 16
  • yes i am using C#, and i tried to copy and past this function, but it seems to have multiple errors. Are you sure this is the auto generated code ? – Nada N. Hantouli Apr 29 '15 at 12:06
  • it is not recognising :AntiXsrfTokenKey in Request.Cookies[AntiXsrfTokenKey]; and also not recognising these variables as well _antiXsrfTokenValue, master_Page_PreLoad – Nada N. Hantouli Apr 29 '15 at 12:08
  • @NadaNaeem i've included the missing constants. – Mahesh Kava Apr 30 '15 at 01:24
  • thank you, but still missing the master_Page_PreLoad, but never mind – Nada N. Hantouli Apr 30 '15 at 07:51
  • There is a little bug here. Assume that the application newly started and there is no antixsrf cookie. A user made 2 requests concurrently (second request began before the first request ends) A token will be generated for the first request and will be written to the antixsrf cookie. "requestCookie != null" will return false for second request because there was no cookie on the client when it began. So another new antixsrf token will be generated and written to the cookie. When the first request posted back it will get an xsrf error. Because antixsrf cookie value changed by the second request. – yvzman May 23 '19 at 13:25
  • How can you do this concurrently? Even browsers like Chrome only allow you 1 request at a time to the same site, even if you open multiple tabs. – ActiveX Jul 08 '20 at 17:00
  • Breaks on a post-back. Unable to cast object of type 'System.Web.UI.ViewStateMode' to type 'System.String' – xizwyck Mar 30 '22 at 17:39
-5

You could use below piece of code, which will check the request where it is coming from

if ((context.Request.UrlReferrer == null || context.Request.Url.Host != context.Request.UrlReferrer.Host)) 
    {
        context.Response.Redirect("~/error.aspx", false);
    }

It works great for me!!!

Bell
  • 17
  • 3
    This is wrong and dangerous. It only limits CSRF to the same host, and can potentially break real requests if the referer header is stripped, which is relatively common. – Steve Aug 29 '17 at 16:25