.Net CSRF Guard
This page, like the tool, is a work in progress. Questions? Contact Jason Axley (Jaxley) or use the Talk page.
- 1 Problem Overview
- 2 Threat Model
- 3 How CSRFGuard works
- 4 Implementation Approach
- 5 Implementation
- 6 Related work
- 7 Completed
- 8 TODO
A realization that I just had today was that it seems that the root cause of CSRF is cookie-based session IDs that get auto-sent by the browser with each request. What CSRFGuard (Java and this .Net version) therefore try to do is to allow one to continue using Cookie-based sessions by layering on top of this yet another session token that isn't sent in a cookie to essentially attempt to authenticate the HTML page contents and links as belonging to a legitimate session.
ASP.Net specific concerns
CSRF can actually be prevented in .Net already -- but you have to be using ViewState. In fact, ViewState prevents many other attacks as a happy coincidence, such as several data input validation attacks for non-text-form fields.
There are problems with relying on ViewState however:
- Developers can disable ViewState per-page or on an entire ASP.Net application. They can also forget to enable it on a key page. Knowing you have protection becomes a laborious code-and-configuration-review-problem.
- ViewState can result in hugely bloated page content if it is not cared for properly during development
- Developers can inadvertently divulge sensitive date in ViewState that may go unnoticed since ViewState is Base64-encoded in the page content
There are also those out there who think that ASP.Net is immune to CSRF already, probably because ViewState is enabled by default so if you try CSRF attacks without knowing this key fact, you may be lulled into a false sense of security (or at least may not appreciate why CSRF doesn't work in a default setup). You can try it yourself easily enough (and I will probably include a demo of ViewState CSRF protection in my sample test web application when the code is released).
Try to submit a CSRF form post to a page with ViewState enabled without the viewstate data. Notice that it does not work. Now, edit the Web.config and add:
<pages enableViewState="false" />
You will find that you can now CSRF attack to your heart's content. Although, even with viewstate enabled, an attacker can still attack you by precomputing the viewstate, if it is the same for multiple users. This can be the case even if viewstate MAC is enabled.
The simple solution, but one that is somewhat brittle since it is so easy for developers to forget and hard to know where it is missing, is to add a viewStateUserKey.
TODO: paste in example
How CSRFGuard works
Incoming Requests: Protection
The module hooks the YYYYYY event and calls on the CSRFGuard object to check the contents of the request for whether to even bother validating the request or not. There are several cases (many configurable) where it makes sense to skip the request (meaning that it doesn't represent a CSRF attack risk)
- case 1
- case 2
- case n
If it determines that the request needs checking, then it compares the passed CSRFGuard Session Token to the one stored in the user's ASP.Net session. If they do not match, or if the token is not present, then we've got a CSRF attempt.
When a CSRF attempt is detected, the Module then decides what to do about it, based in large part to the configuration. There are likely to be many, many possible cases so an attempt will be made to design high-level generic mechanisms that can support various use cases.
Outgoing Responses: Page Rewriting
The module also hooks the ZZZZZZ event and provides a custom Request Filter that interrogates the outgoing HTML so that we can inject our CSRFGuard Session Token
This project takes an analagous approach to the J2EE CSRF Guard J2EE Filter in the ASP.Net world: an ASP.Net HTTP Module / Filter.
- Threat-modeled design and implementation
- Fully test-driven (will be using nunit2)
- And very high code coverage for unit tests (using ncover)
- Automated build (will be using nant)
- Highly configurable
- Allow for flexibility to support all different kinds of applications.
- Highly modular code
- Bulk of the decision-making is done automagically in objects who can then be interrogated for access decisions
- Small classes and methods for clarity and avoiding overly-complex code.
- Ease potential rolling multiple utilities like this into a single uber-HTTP-Module framework later (need to check out status of the mod_security port to .Net)
- No tight coupling to the application required (but may be useful to support for optimizing performance or integration)
- Allow non-security code to be overridden by another implementation (e.g. use your own Logger class, use your own Config file class, support additional response mechanisms, etc.)
- Ability to run in a partial trust environment (hat tip to User:Dinis.cruz)
- Ability to optimize the configuration to just where you most expect to have CSRF vulnerabilities (e.g. avoid work for no security gain)
- Not just a straight code port from J2EE version
Differences between this and the J2EE version (to be resolved)
The CSRFGuard object is the guts that does everything. The CSRFGuardModule is just a shell for hooking the CSRFGuard module in via an httpModule declaration.
Insert UML / Class diags and discussion of design
- PHP CSRF Guard
- J2EE CSRF Guard
- OWASP Enterprise Security API
- Tokens are only added if URLs match a same-origin policy (avoids leaking the token information outside the app)
- CSRFGuard module that is able to detect CSRF attacks.
- Filters to inject the tokens
- CSRFHandlers to handle CSRF attacks
- LogEvent (logs with log4net)
- KillSession (kills the asp.net session)
- RedirectToURL (with optional parameters to denote the error info or message type)
- PrintError configurable message to screen instead of the response text
- Configurable options:
- extensionWhitelistPattern (file extensions to ignore when adding tokens to URLs -- files that would not be usable for CSRF attacks)
- useRandomCSRFTokenName (ability to generate a random token name instead of a static one)
- skipDetectOnParameterlessURLRequests (if there are no POST or HTTP parameters, there may not be a CSRF attack. Allow this to be set for apps where GETs are idempotent)
- ResponseFilter (change which response filter to use at runtime. RegExFilter is the only one done right now).
- logging with log4net
- Configurable evasive actions (CSRFHandlers)
- Make the actions chainable and atomic and configuration-driven so you can execute them one after another to make complex interactions
- Extensible CSRFHandler plugin loading
- Got the code into source control
- config file supports complex data types, such as an arraylist, for lists of data.
- Can build .net 1.1 or 2.0 assemblies for either setup.
- Have a demonstration web app for testing. Will be used for functional tests using WatiN.
- 17 unit tests thus far.
- Ability to debug nunit tests from Visual C# express.
- Document the design and configurable options on the wiki
- Check out J2EE CSRF Guard 2.0 features and nomenclature for possible alignment
- Actually, the current development version of CSRFGuard for .Net already implements many of the requirements that I had shared with the Java team.
- Investigate threats of bad applications that leak ASP.Net session info from HTTPS to HTTP. This module right now is implicitly assuming that the session is trustworthy which it would be nice if we could have some programmatic basis for believing this.
- Write nunit test cases for everything
- Loads of error handling to be production-ready.
- Secure Coding:
- Data input validation on everything
- Data input canonicalization
- Implement relative URL whitelisting (skipDetectForTheseURLs)
- HTMLParser filter
- Need MSI installer
- Need to test out Machine.config deployment
- Need to build both .net 1.1 and 2.0 assemblies for support of either deployment.
- WatiN functional tests.
- Investigate an option for an application to provide its own web control (hidden) that you could override with the value from session instead of replacing the form tags with regex.
- Or, an option that would involve tightly coupling the app and the module -- the module could stick the token in session and the app could put it in the pages; then the module would only need to validate the data coming back and not do any rewriting.