CSRF protection in ring
There are several security attacks that frameworks like Rails try to guard against. In clojure, the choice of using several small libraries instead of a framework means we have to work to protect ourselves. This post will cover how to protect against CSRF attacks.
James Reeves has written ring-anti-forgery, a middleware that will intercept POSTs, PUTs, PATCHes, and DELETEs and check for an anti-forgery token.
The basic example of usage from the readme:
(use 'ring.middleware.anti-forgery
'ring.middleware.session)
(def app
(-> handler
wrap-anti-forgery
wrap-session))
Using ring-anti-forgery requires also using a session middleware in order to save the expected anti-forgery token across requests. Attempting to send a form request at this point should present an error page. Customization of the error is handled by passing options to wrap-anti-forgery as described in the readme.
In order for form requests to succeed, the form needs to include the
anti-forgery token as a parameter. A function
ring.util.anti-forgery/anti-forgery-field
is defined to create html
for a hidden field with the token. Additionally,
ring.middleware.anti-forgery/*anti-forgery-token*
contains the token
for the request, and can be used in your templates. For example, in
clojars I’ve written a
helper
that will automatically add the token. It works as a drop-in
replacement for hiccup’s form-to
(defmacro form-to
"Create a form that points to a particular method and route.
e.g. (form-to [:put \"/post\"]
...)"
[[method action] & body]
`(if (contains? #{:head :get} ~method)
(form/form-to [~method ~action] ~@body)
(form/form-to [~method ~action]
(conj
(form/hidden-field "__anti-forgery-token"
anti-forgery/*anti-forgery-token*)
~@body))))