views:

415

answers:

7

I'm working on improving the security of my company's website and wanted to create a token to prevent forgery attempts that could be easily maintained this is what I came up with.

public class AntiForgeryToken
{
    private readonly string _referenceToken;

    public AntiForgeryToken()
    {
        _referenceToken = Guid.NewGuid().ToString();
    }
    public string ReferenceToken
    {
        get { return _referenceToken; }
    }
}

In my base class for my MasterPage I have a HiddenField wrapped with property named: ReferenceToken

protected virtual void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        InjectToken();
    }

    ValidateToken();
}

private void InjectToken()
{
    var token = ObjectFactory.GetInstance<AntiForgeryToken>();
    ReferenceToken = token.ReferenceToken;
}

private void ValidateToken()
{
    var token = ObjectFactory.GetInstance<AntiForgeryToken>();
    if (ReferenceToken.Equals(token.ReferenceToken, SC.InvariantCultureIgnoreCase)) 
            return;
    ...do stuff for failed token
}

I have StructureMap handle storing the token inside the Session so it's persisted per user session, would all of this be a valid implementation of an AntiForgery scheme?

Edit: There seems to be some confusion on my question, yes I understand ASP.NET MVC has a built in AntiForgeryToken scheme, this question is explicitly about how to recreate this for WebForms to prevent the usage of a CSRF attack (Cross Site Request Forgery). I understand this in no means removes the need for proper authorization of user rights.

I was going to bring up the very link that @Neal and @solairaja posted: Prevent Cross-Site Request Forgery (CSRF) using ASP.NET MVC’s AntiForgeryToken() helper. This article explains more of what the CSRF attack is and how MVC stops it however their solution isn't applicable to webforms which is why I went about implementing my own.

After seeing the response from @Neal I think that will most likely be the accepted answer since I didn't realize I could just get the actual source code from the MVC tool which will most likely replace the guid creation. But I'll leave the question open incase anyone else has some valuable information to add.

A: 

First rule of security: Don't try to roll your own security

As I understand your description, that wouldn't be a valid anti-forgery scheme. All an attacker would need to circumvent it would be to use the View Source feature of his or her browser to find the token. Then he or she could post whatever they like as long as they remember to post that token, and your code wouldn't know the difference.

Apologies if I completely misunderstood your description...

Mark Seemann
I'm pretty sure this is exactly how the Microsoft AntiForgery token is written for MVC that it uses the hidden field on the page. If I was using MVC I would use their functionality regardless but that's not included in web forms.
Chris Marisic
I'm going to have to go with a DV on this since you didn't add any information or provide a solution that already exists to back up your first rule.
Chris Marisic
A: 

First, I suppose I should ask... what do you really mean by "AntiForgery"? What are you concerned about being forged? The rest of what follows is just some general info that pops into mind...

One thing I would change is to not use Guid.NewGuid. There's debate about whether it is random or not and thus not suitable for security purposes. That said, I think it would be a very hard attack to pull off.

Look at RNGCryptoServiceProvider for the GetBytes method for something that should be better for generating a random token. In addition to better randomness, another advantage to this is you can make it whatever size you want.

Are you doing this over ssl though? First off, ssl is line of defense number one for man in the middle attacks. It may not be secure enough (and others can debate about that) for every need, but if you're concerned about such things, it's the starting point if nothing else. If not, how do you ensure that you are getting a response from the right machine and not a man-in-the-middle who is responding first? Without SSL or an equivalent, your token is just as easily stolen as anything else you do.

One additional thing to consider adding is having your tokens be only good for one trip and you generate a new one back to the client on the next trip. Trying to reuse it fails.

I would not try to replace SSL with something else of your own contrivance if that is what you are thinking. If you are concerned about replay though, one time token generation is one way to stop it. If you're worried about a user submitting the same form data twice, this is one thing to do. I would also consider your overall application design if you're concerned about that. Many replay and similar scenarios can be defeated by sound design of your business logic such as not trusting the client to send you sensitive information like the price of an item in a shopping cart.

Please also check out the various microsoft guidance on ASP.NET and IIS security (i.e. Google ASP.NET or IIS security site:microsoft.com) as a starting point. A lot smart people have solved many issues already for us.

Jim Leonardo
Yes this token would be on top of SSL to further prevent replay / man in the middle attacks. I didn't consider the single use token would offer much more robustness both in actual security and just stopping idiots from clicking on stuff before the last postback even finished until you brought this up.
Chris Marisic
The only thing I started thinking about with the single use tokens is what would be an effective way to track valid tokens since a user could have multiple windows open, to keep a list of all tokens out in the session and then on submission to expire the token by removing it from the list so any subsequent attempts to submit the same form would fail until the page finishes it round trip and has a new token stored in the viewstate?
Chris Marisic
A: 

would all of this be a valid implementation of an AntiForgery scheme?

As far as I can tell, it looks like you are inserting a GUID onto a page, and then looking for the same GUID when the page comes back.

I don't know what your requirements are, so I can't say if the scheme is really "valid." However, I can point out a few things:

  1. If the GUID only lives on the page, then what would prevent a user from reading the page on one machine, and submitting the form from another? It would be good to associate it with a cookie or a session (which also uses a cookie), if you aren't already.
  2. If the GUID is written into the page as a static hidden <input> field, then the form could be read and submitted by bots. You can get around that by requiring script on the page to process the token, before submitting it.
  3. Are you using ViewState? If so, you might consider just setting ViewStateUserKey to some repeatable value that's unique per client; it performs a similar function to what you've described here.
RickNZ
"I have StructureMap handle storing the token inside the Session so it's persisted per user session"
Chris Marisic
+3  A: 

Chris,

your approach more or less mimics the anti-forgery approach in MVC, except they use a base64 encoded byte array generated from RNGCryptoServiceProvider and store the token both in the page ( hidden form field ) and in a cookie. I would recommend moving more of the logic into the token implementation ( e.g. encapsulate most of the validation logic inside the token ).

The code for the MVC implementation is freely accessible at http://aspnet.codeplex.com/sourcecontrol/changeset/view/23011?projectName=aspnet#391757 if possible you should probably review that as well as http://blog.codeville.net/2008/09/01/prevent-cross-site-request-forgery-csrf-using-aspnet-mvcs-antiforgerytoken-helper/ for an analysis + ideas.

Neal
I didn't know I could get the source for that! And yes I have much better encapsulation of the injection/validation of the token I just removed all of that for brevity's sake especially when it offers nothing to my actual question. I'll also add the cookie portion to follow their implementation of it.
Chris Marisic
Your current implementation essential dual purposes the session cookie as a placeholder for the anti-forgery cookie so not entirely necessary ( but does reduce session load ).
Neal
A: 

hey Chris,

As NICK told, for me too the code which you have given, looks like you are inserting a GUID onto a page, and then looking for the same GUID when the page comes back. Thats good idea too when ur using a structure map storing into the session.

But there are some inbuilt methods are available for this AntiForgery concepts.

Kindly refer to the below link first and understand

http://blog.maartenballiauw.be/post/2008/09/01/ASPNET-MVC-preview-5s-AntiForgeryToken-helper-method-and-attribute.aspx

Now,

Check the below link for the details descriptions and methodology of approaching.

http://msdn.microsoft.com/en-us/library/system.web.mvc.htmlhelper_methods.aspx

http://blog.codeville.net/2008/09/01/prevent-cross-site-request-forgery-csrf-using-%20aspnet-mvcs-antiforgerytoken-helper/

Thank you !!

solairaja
A: 

Instead of using anti forgery tokens like that I would validate that the authenticated user actually has the necessary rights to make the requested modifications.

E.g. is the web page a "create user page", I would check that the authenticated user has authorization to create new users. Is the page an "edit user X page", I would check that the authenticated user has authorization to modify user X. Maybe because he himself is user X, or an administrative user.

That said, using GUIDs is not very secure. Guids are generated based on an algorithm written for uniqueness, not randomness. AFAIK there are three valid algorithms, name based, time based, and random. If the Guid algorithm used by the system (which could be changed by a future .NET version) is time based, then guessing valid Guids is not very difficult.

Pete
DV since my question is nothing about authorization / user access
Chris Marisic
Pete, please go and read http://blog.codeville.net/2008/09/01/prevent-cross-site-request-forgery-csrf-using-aspnet-mvcs-antiforgerytoken-helper/ to understand the why / how of anti-forgery tokens.A CSRF will submit the users session cookie and therefore look like an authenticated user so long as the attack happens within the scope of an active session. Anti-forgery tokens are designed as one-time use tokens and therefore the exploit window is significantly reduced.
Neal
@Neal - thanks for that link, that was a not aware of that particular vulnerability.
Pete
A: 

That looks to me as if it's generating a canary with every request. Now what happens if a user opens multiple tabs? :)

The problem with your approach (and the ASP.NET MVC implementation) is it relies on developers to implement it. Security like this should be opt-out, not opt-in. When I wrote a AntiCSRF module I ended up using the ASP.NET page lifecycle instead, which mean no changes to the underlying code, unless a developer wanted to opt a page out of the CSRF checks. You'll note that it uses a single token which lasts for the lifetime of that user's browser session - there's no actual need to change the token with every request.

Now I wrote the module mainly as a way to illustrate some concepts for my forth coming book (insert advertisement here grin), you could of course use the ViewStateUserKey, but again this is opt-in, rather than opt-out.

blowdart
I think you also missed where I said the tokens would be stored in session by StructureMap. And the code posted here is for brevity sake as I explained I believe to @Neal, I have much better encapsulation of the injection/validation that it doesn't actually get implemented per page (so it's DRY compliant), it's just irrelevant for my question.
Chris Marisic
My point was you're still relying on developers inheriting from your base page class. If you hook into the page_prerender event via an HTTP module then you don't have to worry about developers inheriting from the right class. It doesn't matter where you store the token you check against (although does StructureMap work in a web farm easily?)
blowdart
I took a look at your module it looks really good, I must've been searching with the wrong keywords on google that I couldn't find this before!
Chris Marisic
I doubt my application will ever need to be hosted in a webfarm anytime soon however if that was the case I would replace my actual session state to be a distributed cache and then possibly might need to write a custom caching provider for SM. I actually have it inside a base class for my master page from which all master pages are derived since it has other functionality that is global to the site.
Chris Marisic
Heh, well I wrote the module to be paranoid. If you're the only developer, and you are always going to use the master page then fair enough. But as soon as someone else starts editing your code ... :D
blowdart
Most of the time I am a fan of convention over configuration. Too many times I've seen configuration hell.
Chris Marisic
Well this is a single configuration line. I'm not sure making sure all your pages use a particular master page is conventional ...
blowdart