tags:

views:

249

answers:

2

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.

+2  A: 

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.

Brendan Kendrick
A: 

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; }
     }

 }
Toran Billups