Sunday, February 13, 2011

Can anyone suggest an abstract base class to prevent XSRF in .NET 2.0+

I'm looking for an abstract base class or master page solution that will prevent anyone from doing XSRF using both a token and ttl. Can anyone point me in the right direction?

Edit: The ideal solution will leverage the cookie that the default membership provider sends down to the client.

  • You could put a hidden field on your masterpage, generate a key during the Page_Load event of your master page, assign the key as the value of your hidden field and then add that value to your cookie. Then you just compare those values.

  • I started a base class that a master page can inherit. I opt'd to use viewstate instead of putting a hidden input down because with this approach I don't need to worry about multiple forms on a page/etc. It also takes a little more work to find this value than a simple "view source"

    The below are a few issues that I'm trying to correct.

    • When I refresh the page (not post-back) the viewstate,and hidden input, (when I started this approach) values are not updated like the cookie is

    • When I navigate to a new page inside my app, the new page starts without the valid viewstate and thus my compare fails for this case ...

    The below is my work in progress ;)

     public class PreventXSRF : MasterPage
     {
    
         HttpCookie mCookie = null;
         FormsAuthenticationTicket mPreviousAuthenticationTicket = null;
         FormsAuthenticationTicket mNewAuthenticationTicket = null;
    
         public bool IsXSRF()
         {
             if ((Request.Cookies(".ASPXAUTH") != null)) {
                 mCookie = Request.Cookies(".ASPXAUTH");
                 //get the current auth ticket so we can verify the token (userData) matches the value of the hidden input
                 mPreviousAuthenticationTicket = FormsAuthentication.Decrypt(mCookie.Value);
             }
             else {
                 ///'the membership cookie does not exist so this is not an authenticated user
                 return true;
             }
    
             //** ** **
             // verify the cookie value matches the viewstate value
             // if it does then verify the ttl is valid
             //** ** **
    
             if ((mPreviousAuthenticationTicket != null)) {
                 if (mPreviousAuthenticationTicket.UserData == Token) {
                     if ((TTL != null)) {
                         if (Convert.ToDateTime(TTL).AddMinutes(5) < DateTime.Now()) {
                             ///'the ttl has expired so this is not a valid form submit
                             return true;
                         }
                     }
                     else {
                         //** ** **
                         // ?? what about a hack that could exploit this when a user tries to BF
                         // a value for the token and simply keeps the viewstate for ttl null ??
                         //** ** **
                     }
                 }
                 else {
                     //** ** **
                     // ?? I hit this when I navigate to another page in the app (GET)
                     // in this event, it was hit because the cookie has a valid token
                     // but the page is new so viewstate is not valid ... ??
                     //** ** **
                     ///'the cookie value does not match the form so this is not a valid form submit
                     return true;
                 }
             }
             else {
                 ///'the authentication ticket does not exist so this is not a valid form submit
                 return true;
             }
    
             //** ** **
             // if the code gets this far the form submit is 99.9% valid, so now we gen a new token
             // and set this new value on the auth cookie and reset the viewstate value
             // so it matches the cookie
             //** ** **
    
             //gen a new ttl and set the viewstate value
             TTL = GenerateTTL();
             //gen a new token and set the viewstate value
             Token = GenerateToken();
    
             if ((mPreviousAuthenticationTicket != null)) {
                 //** ** **
                 // create a new authticket using the current values + a custom token
                 // we are forced to do this because the current cookie is read-only
                 // ** ** **
                 mNewAuthenticationTicket = new FormsAuthenticationTicket(mPreviousAuthenticationTicket.Version, mPreviousAuthenticationTicket.Name, mPreviousAuthenticationTicket.IssueDate, mPreviousAuthenticationTicket.Expiration, mPreviousAuthenticationTicket.IsPersistent, Token);
             }
             else {
                 ///'TODO: if no auth ticket exists we need to return as this won't be valid
             }
    
             if ((mCookie != null)) {
                 //** ** **
                 // take the new auth ticket with the userdata set to the new token value
                 // encrypt this, update the cookie, and finally apply this to the users machine
                 //** ** **
                 mCookie.Value = FormsAuthentication.Encrypt(mNewAuthenticationTicket);
                 Response.Cookies.Add(mCookie);
             }
             else {
                 ///'TODO: if no cookie exists we need to return as this won't be valid
             }
    
             //if we got this far without a return true, it must not be a xsrf exploit so return false
             return false;
         }
    
         private string GenerateToken()
         {
             RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
             byte[] randBytes = new byte[32];
             random.GetNonZeroBytes(randBytes);
             return Convert.ToBase64String(randBytes);
         }
    
         private string GenerateTTL()
         {
             return DateTime.Now();
         }
    
         private string TTL {
             get { return ViewState("TTL"); }
             set { ViewState("TTL") = value; }
         }
    
         private string Token {
             get { return ViewState("Token"); }
             set { ViewState("Token") = value; }
         }
    
     }
    

0 comments:

Post a Comment