I have recently started exploring Clojure and I wanted to set up a simple web app with basic CRUD functionality. I found a nice tutorial here: http://www.xuan-wu.com/2013-09-21-Basic-Web-Application-in-Clojure.
The GET requests work fine, but whenever I attempt a post request, I get the following error:
Invalid anti-forgery token
The tutorial I mentioned earlier does not address anything security related. I did some digging around and it seems like I'm missing some component of Compojure that is supposed to generate a token for making POST requests. Some places mentioned that I was supposed to happen automatically without any changes on my part. I am still not sure what it is that I am missing. Here is my code:
(ns myblog.handler
(:require [compojure.core :refer :all]
[compojure.route :as route]
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]
[myblog.views :as views]
[myblog.posts :as posts]
[ring.util.response :as resp]
[ring.middleware.basic-authentication :refer :all]))
(defn authenticated? [name pass]
(and (= name "user")
(= pass "pass")))
(defroutes public-routes
(GET "/" [] (views/main-page))
(route/resources "/"))
(defroutes protected-routes
(GET "/admin" [] (views/admin-page))
(GET "/admin/add" [] (views/add-post))
(POST "/admin/create" [& params]
(do (posts/create params)
(resp/redirect "/admin"))))
(defroutes app-routes
public-routes
(wrap-basic-authentication protected-routes authenticated?)
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
Again, only the POST request "/admin/create" is failing with the invalid token error. Any idea what I'm doing wrong?
Your problem is with the wrap-defaults and site-defaults setting. The site-defaults default configuration adds ant9forgery CSRF protection and any post request which does not include a valid CSRF-token will be blocked.
There are a couple of ways to get around this
Use api-defaults instead of site-defaults. The api-defaults default config is for sites where you are serving up a web API and turns of the CSRF protection included in site-defaults, which is meant for a more traditional web site where post requests are generated by a form which was previously delivered via a get request and includes the csrf-token as a hidden field. The disadvantage of this solution is that it may also disable other middleware you do want to be included
Disable the csrf protection in the site-default config. This involves setting the appropriate key value in the map to false i.e.(wrap-defaults app-routes (assoc-in site-defaults [:security :anti-forgery] false))
add a hidden field to your form, example use enlive-html:
(ns xxx.html
(:use [net.cgrand.enlive-html])
(:require [clojure.string :as str]
[ring.util.anti-forgery :refer [anti-forgery-field]]))
(deftemplate render-page "base.html"
[settings]
[:form#my-form] (append (html-snippet (anti-forgery-field)))
...
)
or disable it, not recommend, as:
(def app (wrap-defaults app-routes (assoc-in site-defaults [:security :anti-forgery] false)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With