Learned a little something about Cross Site Request Forgery

I'm admittedly a web development and security newb, but I learned a bit about CSRF

What is CSRF?

Open Web Application Security Project (OWASP) describes it as

Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a malicious web site, email, blog, instant message, or program causes a user’s web browser to perform an unwanted action on a trusted site for which the user is currently authenticated.

How does it apply to my website?

When I (re)wrote this blog, I created a RESTful API to deal out JSON to the pages' javascript. In this way, the blog was basically a Single Page Application using AngularJS. I was vaguely aware that the way I had written it, technically anyone could hit the API and retrieve or modify articles as JSON. In the back of my mind I knew that anyone could just POST to that API and create or modify content on my blog. I had some choices.

Even if I chose to get rid of the JSON API, it technically would have been vulnerable to CSRF. In short, this is because someone could craft a form, some javascript, or even a script, and if an authenticated user of my site clicked, visited, or ran it, it would be able to modify my data on their behalf.

As a web page:

<html>
  <head>
    <title>CSRF</title>
  </head>
  <body>
    <form method="POST" action="https://4d4ms.com/blog/articles/new">
      <input type="hidden" name="title" value="Get Rich Quick" />
      <input type="submit" name="submit" value="Look at my nice pictures" />
    </form>
  </body>
</html>

As a malicious script:

curl -X POST https://4d4ms.com/blog/articles/new --data '{"title": "Get Rich Quick"}'

How can you use Ring Middleware to protect against CSRF?

Ring, the excellent clojure package upon which this site is built, has some excellent middleware that will handle generating anti forgery tokens. These tokens are one of the ways you can defend against certain CSRF attackes. The tokens are used to prove that the user that is performing an action is doing so from an expected place (a page on the site itself, for example).

(-> routes
  (wrap-defaults secure-site-defaults))

...

; acquire the anti forgery token from the session
(get session :ring.middleware.anti-forgery/anti-forgery-token)

...

; pass the anti forgery token to populate a hidden field on any form POST you've got
(let [action (or _id "post")]
  [:form {:name "articleForm" :action (str "/blog/articles/" action) :method "POST" :enctype "multipart/form-data"}
    (hidden-field "__anti-forgery-token" anti-forgery-token)

If a form or script does not pass that anti forgery token with the correct value for the corresponding session when it attempts to POST, the Ring middleware generates a response before my route handling code even sees it:

<h1>Invalid anti-forgery token</h1>

You might have a few questions

Take Away

Is anyone trying to modify my site using Cross Site Request Forgery? No. And even if they were, all they could do is insert some content. The point is that I learned how it works and a little bit about how to defend against it for a pretty simple case. It was a fun project made even more enjoyable by the excellent support in Ring.