csrf defenses at app sec usa

speaker: Ari Elias-Bachrach

Overview

  • Most defenses work 80% of the time. Does your app fall into the 80%?
  • CSRF sometimes pronounced c-surf
  • CSRF attack uses browser to perform action without user consent
  • Vulnerable if all params predictable. Then can put url in image tag. Or use JavaScript to submit (need for post)
  • If have multiple tabs or haven’t logged out, your browser thinks authenticated session so can still perform actions.

Patterns

  • Synchronizer token – most common alternative – use a csrf token then have server make sure gets same random value back. Watch how token is remembered and completeness of coverage
  • Double submit cookies – Makes sure cookie and hidden field match. Useful for REST. Do not use session iD for this purpose. Session ID is in the GET request; not secrete
  • Challenge response – CAPTCHAs or reauthentication – do protect against CSRF but usually used for other things. Watch user impact.Never seen for CSRF primarily
  • Check the referrer header – the referrer should be your site not another site like twitter. You don’t perform action on first visit to the site. Watch side effects. Never seen for CSRF primarily.

Code level

(.net) ViewState
Submitted state on all postbacks. Useful for wizards. Add session id to view state for token. Now your view state is unpredictable. Works fine if only use postbacks. If sometimes actions go to other pages, won’t work.

(.net) AntiForgery token
Put Html.AntiForgeryToken() as first scriptlet in form. And add [ValidateAntiForgeryToken] before each action. Problem is by default this only works for POST by default. People have hacked it. If your application is properly designed, GET is idempotent anyway. The worse problem is the “forgetful programmer issue”. A developer can forget to add it to every form. [I wrote a unit test for this problem in Java]

(.net) AntiCsrf
Implements double submit cookies pattern. Only supports POSTS.

(Java) CsrfGuard
PHP and .NET versions in progress. Keeps on token per session by default and uses synchronizer token pattern. Problem is that exposure of token jeopardizes whole session to CSRF. Experimental option to per link/action token. Modifies get and post requests. Supports AJAX via a HTTP header. May mess up links if use funky URLs. [you shouldn’t be using GETs. grumble. It’s not that hard to change GET to POST]

(Java) HDIV – HTTP Data Integrity Validator
Uses synchronizer tokenizer pattern. Session by default. Experimental optional for link/action tokens as well. Queue based expriy. Set queue size accordingly.


Server level

Tomcat
Tomcat 7 generates UUID for each page loaded. Protects GET and POST. Runs as a filter. Don’t have to change app at all. Helpful for an old app that nobody knows about. [seems risky to me. apps have funky javascript, get vs post, etc. Oh good. Jim Manico called out that it can break your app.]

F5 (Load Balancer) ASM
Same ideas as Tomat except it has less visiblity into your session. Can have token expire based on time since can’t go by session expiration. Defaults to 10 minutes.

Imperia SecureSphere
Checks referrer header isn’t from an external site. Can see false positives. Browser plugins can tamper with referrer. Browser won’t include referrer error from SSL. More useful for detecting than preventing. There are plugins that create random referrers so would have to turn off that plugin to use your site. Also, wouldn’t stop stored XSS for CSRF attack.


Other problems

  • CSRF token can reveal which library you are using. If you have an uncovered vulnerability, this could be revealed
  • If you have ANY XSS vulnerability in your site (even on another page), you are not protected against CSRF
  • Protecting GET pages comes at a cost [I’m glad he finally mentioned this]

slides available

My take
CSRF isn’t new to me. See what we did at CodeRanch. I think it is an important topic though and it was interesting to see about other solutions. The speaker was very thorough assuming no knowledge and covering more advanced topics.

csrf for JForum without javascript

In February, I wrote a three part series on how we fixed JForum on coderanch to protect from CSRF.  In included;

  1. Analysis
  2. Extending OWASP
  3. Problems

Remaining problems

Unfortunately, there were three remaining problems.

  1. Some mobile devices weren’t able to handle the JavaScript which added the tokens.  Meaning our site didn’t work on all mobile devices.
  2. The CSRF token was in the URL of thread links which meant people were sharing those links with tokens in them.  This isn’t a significant security risk, but it does confuse Google which is bad for SEO.  It is a tiny security risk in that if someone posts their current CSRF token, that user can be targeted for a CSRF attack until that token expires.
  3. Some people take a really long time to write a post or have lunch in the middle and the token expires.  Giving them a CSRF error page when they finally finish.

This blog post shows how we solved these problems.

Ending requirement for JavaScript to set CSRF token and getting token out of URLS

In general, there are three options for setting a CSRF token in forums/URLs: JavaScript, a filter to change the HTML at runtime before it gets served to the client or hard coding the token.

Based on our experience with mobile devices, we decided not to go with option1 (JavaScript.)  I had used a technique similar to option 2 (changing the HTML as it goes by) to transform our JForum URLs to a different format.  This turned out to be more complex on a forum where users routinely post code.  The same problem would occur here so I decided against that option.  We don’t want to be adding CSRF tokens to code user’s post in questions.  And we certainly don’t want someone to be able to inject a CSRF attack in a post!

This left me with option 3 – hard coding the token.  There were a few steps to implementing this option:

  1. The JavaScript solution added the token to URLs in addition to forms.  This wasn’t “good” in the first place in that URLs shouldn’t be changing database state.  I had recognized this as a shortcoming back in February but lacked the time to fix it then.  There’s a representative list of these pages/URLs on github.  Many were fixed by a one to one conversion of links to forms. (with POST as the method.)  A few were fixed with a generic form on the page and JavaScript that calls it.  We used the generic JavaScript form for some admin links to save bandwidth. Most of these aren’t available in the mobile view anyway.  And moderators weren’t the ones having JavaScript issues in the first place – probably because we have newer mobile devices.
    </span></span>function submitActionForForum(actionVerb, forumId) {
    var action = document.actionForm.action;
    
    action = action.replace("ACTION", actionVerb);
    
    action = action.replace("FORUM_ID", forumId);
    
    document.actionForm.action = action;
    
    document.actionForm.submit();
    
    }
    
    
  2. At this point we don’t need to add tokens to anchors.
  3. Remove AddJavaScriptServletFilter and JavaScriptServlet so tokens are no longer generated by JavaScript.
  4. Add token to all forms:
    <input type="hidden" name="OWASP_CSRFTOKEN" value="${OWASP_CSRFTOKEN!""}" />
    
    
  5. For forms containing multipart/form-data (there were less than 10 of these), add to the URL:(this is needed because the multipart request is only parsed once – later on in the process and the parameter isn’t available to us in the filter)
    ?OWASP_CSRFTOKEN=${OWASP_CSRFTOKEN!""} 
  6. In parallel to the previous two steps, I wrote a unit test to ensure I didn’t miss any required tokens AND to alert anyone adding a form to the codebase in the future about the need to add a token.  Unit test available on github.
The other problem – session timeouts
My first thought was to use AJAX to keep the session active as long as the browser is open.  This session isn’t ideal as some users keep their browser open all day.  I talked to the site owner and we agreed on a three hour timeout for CSRF tokens based on inactivity.  (This also has the advantage of the token surviving a server restart which we do for deployments.)
I implemented by creating a database table with the user id and token.  I have a TimerTask that deletes any tokens over three hours old and runs every 15 minutes.  (This isn’t strictly accurate as it keeps the token alive as long as the user session or the token is less than 3 hours old.  So if I have an active session for 2 hours 55 minutes and a server restart happens, I lose my token.  This implementation may change if it turns out to be a problem in reality.  The current filter and helper class are now online for reference.
What I learned
It’s a lot harder to protect against CSRF on a public website than one with controlled users.  This was an interesting project though.

fixing csrf for jforum and csrf filter analysis (part 1)

This post goes through how we fixed CSRF (cross site request forgery) in JForum, issues encountered and approach.  It is useful reading for anyone who needs to protect against CSRF on their website.

Background

Stock JForum has a number of security vulnerabilities.  We’ve fixed a lot of the XSS ones.  We hadn’t fixed CSRF as of early January 2013.  (It is fixed now – don’t bother trying.)  We had captchas enabled for creating ids.  And I think our working theory was that other CSRF problems would be annoying, but easy to undo.

On January 14th, we learned there was going to be an announcement of XSS and CSRF vulnerabilities  on January 16th (later changed to February – High-Tech Bridge asked me to post this before their announcement.)  I did a code review and learned that someone could exploit a CSRF attack to cause all sorts of damage including deleting the forums – which would require a database restore and likely lost posts from the duration.  Eek.  Nothing like an urgent need to have a release.

What is CSRF

Cross Site Request Forgery is a security attack where the attacker tries to do something in your name.  For example, if you open an email (with images enabled) or go to another website in a tab of your browser while being logged into your bank in another tab, the bad guy could try to move money between accounts for you.  Not something you want.  In a forum, the bad guy could post spam or ads in your name.  Or even delete the forum.  OWASP has an excellent overview of CSRF along with suggestions of “fixes” that do and don’t work.

Analysis

The first thing I did was write code (AllJForumActions.java) to output the names of all actions in JForum that can be accessed from a browser.  To see how much analysis was required.   In stock JForum, the 112 such method names are listed here..  In JavaRanch/CodeRanch forked JForum, there were 191.  (we’ve added a lot of functionality)

I then classified them in a property file by whether they were safe (read only) or needed protection.  I wrote a unit test to check this property file:

  • has all action names represented
  • doesn’t have any action names more than once
  • only lists valid case.
A sample property file shows the expected format.  The unit test helps future proof the CSRF functionality in addition to checking for typos.  (With almost 200 actions, it is easy to miss something by hand.)  Unit test failures direct developers to a few paragraph guide I wrote up on whether the new action needs CSRF protection.

Choosing a CSRF filter

Ok.  Analysis done.  Now on to coding. CSRF is a common problem.  I’d like to use an existing tested filter and just adapt it to JForum.

1) Google code CSRF Filter (StatelessCookieFilter)

From website: this one says it is better than the OWASP one by being simpler and using cookies rather than session (memory) usage.

Analysis

  • It is simpler, I’ll give them that.
  • I’m not terribly concerned with the memory implication.  It’s just one token per user.  We store other things in the session in memory.
  • Disadvantage: the code requires you to manual add the CSRF token to all POST forms.  That’s time consuming and error prone.
  • HUGE disadvantage: the filter assumes that only POST methods need to be protected.  This is only true if you strictly enforce not accepting GET requests for any requests that change state.  While I agree all code should be that way, it isn’t in the real world.  Many frameworks (including JForum) treat get and post requests the same by directing them to the same code.  If I was using this code, I’d need to change it avoid the check for POST.  (albeit not a big deal since it is less than 100 lines long).  We’d need to protect both get and post actions and exclude the expected GET URLs from the filter.  This checks the expected POST actions regardless of their actual submit type.
Summary: the code is smaller and simpler because it does a lot less.
Appears to only support “token per request.”  I want token per session.  It’s less secure than per request, but users on coderanch do a lot of “let me open 20 tabs at once.”  Besides, we aren’t running a bank here.

3) OWASP filter

Well documented.   Supports many types of checking.  Sets up Javascript DOM injection of token so don’t have to hand edit each page.  It supports a choice of only injecting the token into forms or whether to include src/href links as well.  This is great as I know that each and every update to the database isn’t in a form.  I’m also confident the developers know what they are doing because the documentation says:

 However, most JavaEE web applications respond in the exact same manner to HTTP requests and their associated parameters regardless of the HTTP method. The risk associated with not protecting GET requests in this situation is perceived greater than the risk of exposing the token in protected GET requests. As a result, the default value of this attribute is set to true. Developers that are confident their server-side state changing controllers will only respond to POST requests (i.e. discarding GET requests) are strongly encouraged to disable this property.

Comparing this to the google code which only checks POST, it is a very easy decision which one to go with.  The one that actually secures you against CSRF in the face of code that isn’t perfectly written.  OWASP CSRF Guard!

Starting out with OWASP CSRF Guard Version 3

The documentation was clear and self explanatory so let’s get into the problems I encountered:

  1. Minor problem: the OWASP CSRF Guard source code from github isn’t the same with the download offered on owasp.  It was an easy Ant build from the github version.  I rebuilt because I needed to call a method that existed in the github version but not the download/packaged one
  2. Major problem: JForum doesn’t consistently use URL names for actions.  There are a lot of Ajax calls that pass the “url name” as a parameter so I can’t filter these out through the OWASP API.    And the “unprotectedPages” logic in the OWASP filter is deep within the component in a final class.  Very secure, but I can’t extend it so must find another approach.
Clearly customization will be needed.  Which brings us to part 2 – extending the OWASP filter or skip right to part 3 – changing JForum.