csrf – extending the owasp solution and “interesting” IE javascript bugs (part 2)

While implementing CSRF for JForum, I needed to extend the OWASP solution.  Let me tell you, they don’t make it easy to extend.  Lots of final.  Here’s what I did – linked to code on github.

To read about the original problem or why I choose the OWASP filter, see part 1.

Extending the OWASP solution

  1. CsrfFilter.java – The OWASP filter is final so had to copy the logic in my own.  I added logic to get the action method name/
  2. CsrfHttpServletRequestWrapper.java – Since I’m using actions instead of URLs, I need to make the request look as if it the actions are URLs.  A short simple class.
  3. CsrfListener.java – OWASP assumes you are using URLs and you can enumerate them in the property file.  I have action names and there a lot of pages to allow as unprotected – without wildcard patterns to help, this is unwieldy.  The OWASP listener isn’t final but the logic I needed to extend was in the middle of a method.  So I copied their listener adding a method that reads the csrf.properties and creates “org.owasp.csrfguard.unprotected.” properties for all lines that weren’t set to “AddToken.”
  4. Redirect.java – the OWASP Redirect class works just fine if you always have the same context root.  We have a different one when testing locally vs on the server.  And since the path is in a property file, it isn’t easy to change.  Of course the OWASP class was final so I had to copy logic rather than extending it.  My Redirect class gets the server and context root from the request and adds it to the relative path in the OWAP property file.
  5. AddJavaScriptServletFilter.java – Adds the OWASP JavaScript to each page after </head> unless it is a download or a user isn’t logged in.  Since logged in users can’t do anything CSRF exploitable, they don’t need the token.  This also makes Google happier because googlebot doesn’t see the extra parameter confusing the URLs.
  6. AjaxHelper.java – The OWASP filter adds “OWASP CSRFGuard Project” to the X-Requested-With header for AJAX calls.  You can customize it to not include that, but I wasn’t sure what would happen if I left it blank.  It was easy enough to change JForum to use startsWith instead of equals() to make it more flexible.
  7. Owasp.CsrfGuard.js (attempt 1)
    1.  The JavaScript provided by OWASP has a bug.  It goes through the properties in document.all from top to bottom.  As it finds forms, it adds hidden input fields.  So far so good.  The problem is that when you have a lot of forums, some of them get bumped past the original end of the document.  So the loop never sees them and the CSRF token is never added.  The solution is to go through the document from bottom to top.  I’ve submitted a fix to OWASP for this.  What did the trick for understanding the problem was adding console.log(“length: ” +  document.all.length);  at the beginning and end.  It grows by roughly 100.   The bug fix was changing in injectTokens()
      for(var i=len -1; i>=0; i--) {

      to

      // BUG FIX: if go through in ascending order, the indexes change as you add hidden elements
      
      // when have 100 forms, this moves some of the forms past the original document.all size
      
      for(var i=len -1; i>=0; i--) {
    2. IE doesn’t like
      var action = form.getAttribute("action");

      when you have a form field named action.  (If at all possible, DON’T DO THAT!!!).  Since JForum has 100’s of such reference, changing it at this point isn’t an option.  IE helpfully returns some object.  It’s not an array/map or list of any kind that I can tell.  After getting hopelessly stuck on this, I asked Bear Bibeault for help.  He noted the answer was in “Secrets of the JavaScript Ninja” a book he wrote that I had recently read.  And it was.  Solution

      // hack to test if action is a string since IE returns [object] when action in form and as hidden field
      
      // if not a string, assume it is our action and add token for now
      
      var action = form.getAttributeNode("action").nodeValue;
    3. The OWASP code contains this innocent looking code.
      var hidden = document.createElement("input");
      
      hidden.setAttribute("type", "hidden");
      
      hidden.setAttribute("name", tokenName);
      
      hidden.setAttribute("value", (pageTokens[uri] != null ? pageTokens[uri] : tokenValue));

      . It works fine in all browsers except IE. Want to know what IE does? That’s right.  It generates a hidden field with the correct token value and *no* token name.  I found this problem solution online and changed the code to

      
      var hidden;
      
      try {
      
      hidden = document.createElement('<input type="hidden" name="' + tokenName + '" />');
      
      } catch(e) {
      
      hidden = document.createElement("input");
      
      hidden.type = "hidden";
      
      hidden.name = tokenName;
      
      }
      
      hidden.value = (pageTokens[uri] != null ? pageTokens[uri] : tokenValue);
      
      
    4. My last IE change was an easy one.  Easy in that it was a problem I had seen before.
      var location = element.getAttribute(attr);

      changed into

      //var location = element.getAttribute(attr);
      
      // hack - getting same error as on action - don't know why but hack to move forward
      
      var attr = element.getAttributeNode(attr);
      
      var location = null;
      
      if ( attr != null) {
      
      location = attr.nodeValue;
      
      }
      
    5. Then I encountered an issue with the event handling framework in IE>  I didn’t even look at this.  All IE debugging was done between 9pm and midnight January 28th-30th after we all thought CSRF was fine from being in production the previous Sunday.  The only computer I had access to that ran IE is from 2002 and barely runs.  I was tired of IE and didn’t want to throw any more time down that rabbit hole.
  8. Owasp.CsrfGuard.js (attempt 2) – After encountering numerous IE problems, I would up merging the code from OWASP CSRF Guard version 2 and 3 to make it less dependent on the browser.

Finally, we get to look at the JForum specific parts in part 3.

more postgres tuning in jforum

A teammate installed a new feature on CodeRanch JForum that uses a 4,515,409 row table. When dealing with over a million rows, scans become a huge performance drain. To the point where one query was slow but real usages with many at the same time brought down the app. The reason why the query was slow was interesting so I asked him if I could blog about it.

The original query

select countryCode, countryName, region, city
     from ip2location
     where 540815125 >= low and 540815125 <= high;

Running this through explain says it uses the index with a cost of:

Bitmap Heap Scan on ip2location (cost=5949.66..54170.71 rows=219870 width=32)

That’s a really high cost explain plan.

My first thought was to change it to:

explain  select countryCode, countryName, region, city
     from ip2location
     where 540815125 >= low and 540815125 <= high;

Which has a much better explain plan of

Index Scan using ip2l_low_asc_idx on ip2location (cost=0.00..8.77 rows=1 width=32)

The reason is that in the first query, postgres needs to scan the large index from the beginning until it hits the low value. In the second, I gave it permission to start really close to the target row. I subtracted 1000 but that was arbitrary. It just needs to high enough to be in the vicinity of the row without missing out on any data.

My approach also makes the lookup time consistent. It is always looking through 1000 worth of index. (Which is always less than 1000 rows given the bunching of low to high.) The original is immediate through a full index scan.

Then the original teammate switched it to:

select countryCode, countryName, region, city
     from ip2location
     where  low =
           (select max(low)
            from   ip2location
            where 540815125 >= low)

This has the same explain cost as the hacky one and is clearer.  Same logic though – it doesn’t require scanning/returning extra results.

TSS Symposium Preview – Throw Away All The Rules. Now What Process Do You Follow?

I'm Speaking at TheServerSide Java Symposium As previously mentioned, Scott and I are both be presenting talks at TheServerSide Java Symposium in March. In preparation for the conference, we are providing sneak peaks of talks this week on the blog.  Scott gave a sneak preview of his GWT lecture.  He has so much information it didn’t all fit in his slides!

I’m giving a talk titled Throw Away All The Rules. Now What Process Do You Follow?” Here are just a few points from my upcoming conference talk:

  1. Testing – for anyone who knows me it isn’t a surprise that I consider regression critical.  But it isn’t always easy to get started.  And yet, the pain of not having it is large.  See how a testing strategy can evolve.
  2. Deployment – a repeatable deployment sounds like a corporate bureaucratic thing.  Yet knowing what is in production and how to get there quickly is an agile concept.
  3. Leader – Note I said leader and not project manager.  Even if you don’t have a project manager, someone needs to be running the show.  Or in the case of Scrum – the team is the leader.

Hope you enjoyed this sneak peak of my conference talk.  For more, consider coming to my session at the conference. If you register with the coderanch discount code, you can save $200.