getting computer vision to work on a raspberry pi

This year, I helped a local high school FIRST robotics team get computer vision working on a Raspberry Pi.   Two students worked on the problem.  At the beginning, they both worked on computer vision.  Later, one specialized on the Pi and the other on CV.  I learned a lot from a book and from playing with it.  We encountered some interesting and memorable problems along the way.

Recognizing a target

We started with sample code from another team from last year.  This helped learn how to write the code and understand the fundamentals.  It also helped with the “plumbing” code.  As hard as it was to recognize a target, this didn’t prove to be the most frustrating part.  The Pi itself mounted a number of challenges.

Parts for the Pi

We bought/scavenged parts for the Pi.  A USB keyboard, USB mouse and cell phone micro charger where donated.  A HDMI/DVI cable we needed to buy.  We borrowed a computer monitor and ethernet cable.

Finding the right jars

The Pi is built on ARM.  We needed javacv-0.2-linux-arm.jar.  It turned out there is no linux arm version in the latest javacv (0.3).  There is one in 0.2 which we used.  Which was incompatible with the versions of other tools.  (see next problem.)

Setting up the Pi

Compiling opencv on the pi takes 4 hours.  Since that’s how long a meeting is, this meant running the compile overnight.  Having to wait overnight to find out if something worked was like a taste of what punchcard programmers had to go through!

Then it turned out we couldn’t even use our compile.  We were missing the libjniopencv_core.so file. We spent a few days trying to solve this.  We wound up  using a pre-compiled for Pi version.  This is how we got version compatibility.

Updating a NetBeans Ant script

Since speed matters in a competition,we wanted to change the build run target to not compile first.  Netbeans comes with an empty looking build.xml and a useful build impl xml file.  (This is actually my favorite feature of NetBeans – that the build can easily be run outside of NetBeans.)  We easily found the run target in the build impl file.  We copied it to build.xml, renamed it and removed the compile dependency.  This wasn’t actually a problem, but it was interesting how NetBeans sets up the build file.

Starting a script on startup

We wanted the Pi to start the computer vision script automatically on boot up.  We created a file in /etc/init.d since this is a Linux (Debian) install.  Then we made a fatal error.  We forgot to add the & to run the script in the background.  So when we tested rebooting, the Pi hung.  And we couldn’t SSH to it because it hadn’t booted.  The solution was to take the pi’s sd card to another computer and edit the bootup script to use single user mode.  We could then login and edit the startup script to add the missing &.

Networking

We used Java sockets to transfer the “answer” from the Pi to the robot.  The answer being a single number representing the number of degrees off from the center of the target.  We made the mistake of testing this with both ends on a regular computer.  When moving to the robot it didn’t compile because the robot uses J2ME.  We then refactored to use the mobile version (code here).

Performance – CPU

Success.  Computer vision works.  The problem is it took 3 seconds per image.  We reduced it to 1.3 seconds per image by reducing the resolution to the smallest one the camera supports.  We shaved off another 0.1-0.2 seconds by turning off file caching in ImageIO.  We learned the problem was full CPU usage when calling ImageIO.read.

I found an interesting thread showing the “old way” of creating a JPEG using ImageIcon was much faster.  We tried it and the thread was right.  It even created an image we could open in a photo editor.  The problem is that it didn’t work with our code/libraries for image processing.  We don’t know why.  Obviously ImageIO has some default we are unaware of.  A 1 second time is acceptable so this isn’t  a problem.  But it’s an interesting conundrum.

Another interesting CPU note.  We tried compiling image magik.  It took over 3 hours on the Pi.  By contrast, it took 2.5 minutes on a Mac.

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.

csrf – jforum cleanup and problems

See part 1 for how we got here  and part 2 for how we changed the OWASP filter.

Code cleanup and problems

There is some poorly written code in JForum that CSRF now prevents from working.  In these cases, I needed to clean up our code.  For example:

  1. Links/anchors shouldn’t be used to update state.  They should only be used for gets.  The OWASP filter is realistic enough to recognize links are in fact used for updates in the real world and accomodates them.  It doesn’t accommodate code like
    onclick="document.location = '${contextPath}/${jforumMainServletMapping}/${moduleName}/...';" />

    In the cases this was used for actions that required CSRF protection, I needed to refactor to use a form.  In a few cases, I created a one to one form.  On others where there were a lot of actions or it wasn’t conducive, I used a common form

    <!-- CSRF token messes up submission if forum id isn't in URL -->
    
    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();
    
    }
    
    </script>
    
    <#-- general purpose form for the submit buttons on the screen; see JS for ACTION and FORUM_ID values -->
    
    <form method="post" name="actionForm" action="${contextPath}/${jforumMainServletMapping}/${moduleName}/ACTION/FORUM_ID">
    
    </form>
    
    ...
    
    onClick="submitActionForForum('up', ${forum.id})"
    
  2. Some pages (like the first one I happened to test) didn’t have </head> so dynamically adding the JavaScript which sets the CSRF token wasn’t working.  I wrote a unit test to identify such files and ensure we don’t create any more.  We also added the missing HTML headers.  Most of them were on the admin pages.
JForum changes
JForum has some features that don’t play well with OWASP CSRF Guard
  1. JForum’s controller framework is heavily dependent on the number of query parameters . Adding a CSRF token broke all sorts of things.  I solved this by adding a check that the parameter wasn’t the CSRF token to the loops in WebRequestContext’s constructor, parseFriendlyURL()  and handleMultipart().  The one in handleMultiPart is unnecessary and just there for consistency.  I also have the constructor calling a method to see if the query string is empty instead of a one liner:
    private boolean isQueryStringEmpty(HttpServletRequest superRequest) {
    String queryString = superRequest.getQueryString();
    
    if (queryString == null || queryString.length() == 0) {
    
    return true;
    
    }
    
    // ignore OWASP token (so if only OWASP present, ignore it)
    
    return queryString.matches("^&?" + CsrfFilter.OWASP_CSRF_TOKEN_NAME + "=[-0-9a-zA-Z]+$");
    
    }
  2. I then learned that multipart reads the input stream and can’t be called twice.  One approach would be to copy the stream.  I didn’t go that route.  The only multipart requests are posts and PMs.  Both of which need CSRF protection.  Instead I have the filter set to just assume multi part requests need CSRF protection.
  3. Added <noscript> on post_form.htm to explain that JavaScript is now required to post.
  4. A couple places were missing <form> around input fields.
  5. In one place, a jQuery form needed the token set explicitly since it was choosing the parameters for jQuery to send on.  (I think this was only in our JForum fork.)
  6. The CSRF filter requires AJAX to pass the token has a header not a parameter.  To add it to a jquery.ajax block:
    headers: {'OWASP_CSRFTOKEN': '${OWASP_CSRFTOKEN!""}'},
    

    This syntax uses Freemarker’s default value to use a space instead of blowing up if there is no token.

Note to users
I was worried about users seeing the CSRF screen.  Some users won’t know what CSRF is.  And most wouldn’t know what to do.  Feel free to read our error page which includes things to try and who to email.
Enhancements/Longer term goals
We started these but it will take a while
  1. Don’t use token for anonymous users
  2. Get rid of CSRF Token from the  URLs (use forms consistently for posts)
  3. Rollback any hacks we made to fix things/cleanup code.
  4. Reconsider using JavaScript to add the tokens.

Note: all code is sanitized to remove references to javaranch/coderanch.

 

The story continues in part 4 – deciding not to use JavaScript for the solution.