ValidateAntiForgeryToken in WebForms Application
Asked Answered
T

3

10

I have done some reading about the use of ValidateAntiForgeryToken to prevent XSRF/CSRF attacks. However what I have seen seems to relate only to MVC.

These are the articles I've seen:

ValidateAntiForgeryToken purpose, explanation and example

CSRF and AntiForgeryToken

XSRF/CSRF Prevention in ASP.NET MVC and Web Pages

How can I implement this or something similar in a WebForms Application?

Termagant answered 13/9, 2018 at 13:52 Comment(0)
T
7

I found this article How To Fix Cross-Site Request Forgery (CSRF) using Microsoft .Net ViewStateUserKey and Double Submit Cookie with the following information code and instructions:

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.");
        }
    }
}

}

Termagant answered 16/9, 2018 at 10:39 Comment(1)
This approach has the advantage of not using the Session object. If you are developing an App that needs to run on multiple servers, retaining state on the server side prevents you from distributing load across the servers.Dzungaria
B
14

CSRF attacks are not exclusive to MVC application, webforms are vulnerable too.

Basically, CSRF attack exploits the trust that a site has in a user's browser, by requesting or posting information to the website, generally through hidden forms or JavaScript XMLHttpRequests within a the malicious website, as user using cookies stored in the browser.

To prevent this attacks you will need an antiforgery token, a unique token sent within your forms, that you need to validate before trusting the form's information.

You can find a detailed explanation here.

To protect your webforms apps against CSRF attacks (it's working in my projects), is to implement it in your master pages, like this:

Add new Class that will handle the CSRF Validations for you:

public class CsrfHandler
{
    public static void Validate(Page page, HiddenField forgeryToken)
    {
        if (!page.IsPostBack)
        {
            Guid antiforgeryToken = Guid.NewGuid();
            page.Session["AntiforgeryToken"] = antiforgeryToken;
            forgeryToken.Value = antiforgeryToken.ToString();
        }
        else
        {
            Guid stored = (Guid)page.Session["AntiforgeryToken"];
            Guid sent = new Guid(forgeryToken.Value);
            if (sent != stored)
            {
                // you can throw an exception, in my case I'm just logging the user out
                page.Session.Abandon();
                page.Response.Redirect("~/Default.aspx");
            }
        }
    }
}

Then implement this in your master pages:

MyMasterPage.Master.cs:

protected void Page_Load(object sender, EventArgs e)
{
    CsrfHandler.Validate(this.Page, forgeryToken);
    ...
}

MyMaster.Master:

<form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <asp:HiddenField ID="forgeryToken" runat="server"/>
    ...
</form>

Hope you'll find this useful.

Bobsledding answered 14/9, 2018 at 7:31 Comment(0)
T
7

I found this article How To Fix Cross-Site Request Forgery (CSRF) using Microsoft .Net ViewStateUserKey and Double Submit Cookie with the following information code and instructions:

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.");
        }
    }
}

}

Termagant answered 16/9, 2018 at 10:39 Comment(1)
This approach has the advantage of not using the Session object. If you are developing an App that needs to run on multiple servers, retaining state on the server side prevents you from distributing load across the servers.Dzungaria
R
1

Using WebForms, the best thing to do is leverage the ViewStateUserKey.

Here is how to do it...

void Page_Init(object sender, EventArgs args)
{    
    ViewStateUserKey = (string)(Session["SessionID"] = Session.SessionID);
}

It seems kind of strange to save the SessionID in a session variable, but this is needed because it will auto-generate a new ID when empty.

Rsfsr answered 14/9, 2018 at 16:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.