Monday, June 3, 2013

Cross Site Request Forgery (for web forms)

So Cross Site Request Forgery is a web security hole. It centers around tricking a user into going to a malicious page that has knowledge about the target system. The malicious page then makes requests to your sites using your authentication. This basically allows them to do whatever they want using your authentication and knowledge of your system.

The defense is an anti-forgery token. This is essentially a piece of unguessable data that is placed on your page. When a request is POSTed the server can check to confirm that this piece of data, or token, is there. The token can not be acquired by a malicious page so malicious pages can not make successful requests despite the fact that they can send a valid authorization cookie.

The malicious page can actually request the page with the token on it, but the browser will prevent that page from accessing this cross domain data unless the server indicates that it is allowed.

The Microsoft MVC approach involves storing the request token in the cookie and comparing the cookie value with the POSTed data value. Neither the cookie nor the token in the page can be accessed although the cookie is going to be sent. This is done so that the token does not need to be stored on the server in session making this more scalable.

For the project I am working on I put together a very quick AntiForgeryChecker for the web forms part of our application. To use it you add a HiddenInput field on your page:

<asp:HiddenField ID="antiforgery" runat="server"/>

and then make a call in your page load like:

AntiforgeryChecker.Check(this, antiforgery);

The actual object used is below. It uses session instead of the cookie, because we are already using session.

public static class AntiforgeryChecker
{
    public static void Check(Page page, HiddenField antiforgery)
    {
        if (!page.IsPostBack)
        {
            Guid antiforgeryToken = Guid.NewGuid();
            page.Session["AntiforgeryToken"] = antiforgeryToken;
            antiforgery.Value = antiforgeryToken.ToString();
        }
        else
        {
            Guid stored = (Guid) page.Session["AntiforgeryToken"];
            Guid sent = new Guid(antiforgery.Value);
            if (sent != stored)
            {
                throw new SecurityException("XSRF Attack Detected!");
            }
        }
    }
}

9 comments:

  1. Simple and quick way. Really appreciate.

    ReplyDelete
  2. Hi my question is how to use as a common solution for all pages. Suppose I have a project that has 100 .aspx pages and my task to resolve XSS vulnerability on application level. In such case I must not make changes in all pages but should find common firing method where I can implement common solution. any help?

    ReplyDelete
  3. I am a little rusty on aspx, but you should be able to use a master page.

    ReplyDelete
  4. I have got your point but issue with my web app is I haven't used master page on all pages. I am working on to create dynamic hidden field on all page but since I have different kinds of controls on all pages I am failing to create dynamic control and add to all pages when page load. Couldn't find the solution yet.

    ReplyDelete
    Replies
    1. Try putting in Global.asax on this event:

      Application_BeginRequest: Fired when an application request is received. It is the first event fired for a request, which is often a page request (URL) that a user enters.

      Not sure if works. I hope it helps,
      Ricardo

      Delete
  5. Hi
    This is an very short and good
    But when i try to call in page load as

    Antiforgerychecker.Check(this, antiforgery);

    I getting error on first argument "cannot convert from "XXXXX" to "System.web.ui.page"

    Can any one help me

    ReplyDelete
  6. This happens because your first argument to check is not a page. Hard to diagnose beyond that, but if you were using MVC this would fail because you would be passing a controller. If you structured your code so that the call was made from a sub object this would also happen. You could try pulling the page from the HttpContext instead of using 'this'. Good luck.

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete