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.
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.
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:
- 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
- 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.