Re: POST in Ajax - Invalid anti-forgery token

766 views
Skip to first unread message
Message has been deleted

James Reeves

unread,
Dec 23, 2016, 10:36:46 AM12/23/16
to clo...@googlegroups.com
The "use" function is a deprecated shortcut for "require" and "refer". I can see why it would be confusing, so I'll change the examples on the ring-anti-forgery site.

Packages can contain many namespaces, so in general you won't find package names matching up precisely to the namespace or namespaces that they provide.

Regarding CSRF protection, you firstly only need it if you have user authentication in your app. If that doesn't matter, you can turn it off.

If you do have user authentication, then read on.

It's difficult to produce a library that does everything automatically, because it requires cooperation between server-side and client-side code. Ring just handles the server-side, so it's up to you, or another library, to handle the client-side.

I'm not sure how to explain the general functionality any better than the README of the project already does:

Any request that isn't a HEAD or GET request will require an anti-forgery token, or an "access denied" response will be returned. The token is bound to the session, and accessible via the *anti-forgery-token* var.
 
By default the middleware looks for the anti-forgery token in the "__anti-forgery-token" form parameter, which can be added to your forms as a hidden field. 
 
The middleware also looks for the token in the "X-CSRF-Token" and "X-XSRF-Token" header fields, which are commonly used in AJAX requests. 

In terms of what that specifically means for you, you need to do two things:
  1. Add the value of the *anti-forgery-token* somewhere your ClojureScript can find it.

  2. Add the value to the "X-CSRF-Token" header each time you send an AJAX request.
There may be libraries that handle the client-side for you. Maybe someone else can suggest one. The specifics really depend on how your app is put together.

- James


On 23 December 2016 at 14:39, Seth Archambault <seth...@gmail.com> wrote:
So when I use cljs-ajax to post to my APP I get an Invalid anti-forgery token error. 

Despite there being dozens of posts about this issue, none of them have a solution that seems to work if you started your project using this:

lein new reagent myapp

The first post that comes up as a "solution" actually recommends disabling CSRF protection, which seems like just avoiding the problem rather than solving it, but they don't actually tell you how to do that either!

After 4 hours of searching for any solution, I admit that the only fix that I found was this:

(ns reformdems.middleware
  (:require [ring.middleware.defaults :refer [site-defaults wrap-defaults]]
            [ring.middleware.json :refer [wrap-json-params]]
            [prone.middleware :refer [wrap-exceptions]]
            [ring.middleware.reload :refer [wrap-reload]]))

(defn wrap-middleware [handler]
  (-> handler
      (wrap-defaults (merge site-defaults {:security {:anti-forgery false} :params {:keywordize true}}))
      wrap-exceptions
      wrap-reload
      wrap-json-params))


By setting :security :anti-forgery to false, I no longer get the Anti-Forgery issue. But yeah, this isn't a real solution.

What I'm looking to do is get one of the solutions involving "*anti-forgery-token*" or (anti-forgery-field) working.

Unfortunately, these instructions here, don't work, and only produce hard to understand errors:
https://github.com/ring-clojure/ring-anti-forgery

As a side note, it's really confusing when all the libraries use this language:
(use 'ring.util.anti-forgery)
(anti-forgery-field)

Meanwhile "use" is not in any of the code generated using lein. I would expect this would be the proper way to use the libraries:

(ns myapp.server
  (:require 
            [ring.util.anti-forgery) :refer [anti-forgery-field]]))

Of course, I'm just guessing at that refer command, but my point is this inconsistency makes me feel like I'm looking at solutions designed for an older version of Clojure or something?
 

I'm also concerned that given a library [ring/ring-anti-forgery "1.0.1"] there is no way to intuitively know how to "require" that library in your code. In the above situation the word "util" gets added when requiring it..

Anyways, my main question is this:
What is the modern way to do CSRF protection?

Secondly, I'm concerned I'm doing something wrong - ring.middleware feels like a magical thing where you apply wrappers, and then stuff just magically works. I'm not a big fan of magic, I want to be able to see a pathway for finding a solution, not just google around and figure out a wrapper needs to be added.. Any recommendations on that front?

Thanks!
Seth

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Message has been deleted
Message has been deleted

James Reeves

unread,
Dec 23, 2016, 1:03:01 PM12/23/16
to Seth Archambault, Clojure
Another way of introducing the anti-forgery token is to bind it to a javascript variable.

  [:script "var antiForgeryToken = " (pr-str *anti-forgery-token*)]

In ClojureScript, you can then access the token with js/antiForgeryToken.

The Ring-Defaults library exists to provide a way of applying common middleware in the right order. Certain middleware depend on other middleware, so the order in which they're applied to a handler is important. For instance, wrap-anti-forgery depends on wrap-session.

The wrap-defaults middleware just takes a map of options, and then uses this to decide which middleware to apply. The order in which the middleware is applied it handled for you. It also provides some default maps of options that you can adapt.

Regarding *anti-forgery-token* being unbound, this is because it's a binding. If a var has "earmuffs", like *foo*, it indicates it's designed to be used with the binding macro. For example:

  (declare *foo*)   ;; like def, but doesn't have a value yet

  (defn print-foo []
    (println *foo*))

  (binding [*foo* "Hello World"]
    (print-foo))    ;; prints "Hello World"

The value of *foo* is passed into print-foo without needing to be passed as an argument. Any function inside the binding can access *foo*.

This is useful property for *anti-forgery-token* to have, because it means we don't need to manually pass the token to the function that returns the response.

The anti-forgery token is randomly generated for each request you make, then added to both the session (automatically) and the page you return as a response (manually). The idea is that this ensures two things:

1. The user is logged in
2. The user has come from a page you control

You mention that Clojure rewards investment into its fundamental building blocks. That's very true, and at this point unfortunately a little unavoidable! Fortunately the building blocks are fairly simple IMO, and once you understand them you should make more rapid progress.

Also, don't be afraid of looking at source code. Clojure libraries are frequently both small and "flat". You don't get the dense hierarchies of class structures that tend to occur in OOP.

When I was using Ruby and Python it was extremely rare for me to look at the source code of libraries, for exactly the reason you mention. But in Clojure I almost always look at the source code of any library I use. Some are still complex, but most I find to be understandable even if I'm just skimming.

Clojure web development is currently made up of small, specialised libraries. I think overall it's easier for me to understand what's going on in Clojure as opposed to something like Ruby. On the other hand, because the pieces are isolated, tasks like CSRF-protection that require coordination between the server and the client become difficult.

Arachne is a promising attempt to make a web framework for Clojure. Another project that's slowly heading in that direction is my own Duct project, though by smaller increments. I know Luke expects an alpha of Arachne out by the end of January, and the next version of Duct should be out at around the same time.

- James


On 23 December 2016 at 16:49, Seth Archambault <seth...@gmail.com> wrote:
Okay, armed with the new clarity, I felt embolden to tackle this once more. Here's the next steps for those trying to tack CSRF protection onto a Reagent project created with lein new reagent. 

In handler.clj - you'll add the ring.middleware.anti-forgery and refer to *anti-forgery-token*

(ns myapp.handler
 
(:require
           
[ring.middleware.anti-forgery :refer [*anti-forgery-token*]]))


Then, lower down in your code you'll have something like this:

(html5
 
[:head
   
[:meta {:csrf-token *anti-forgery-token*}]


This will create a token for you.  

Now I haven't gone the rest of the distance with this problem so I can't say that it's all downhill from here, it's possible I'm messing up and re-generating a token that won't match what's already matched in the session. Let me know if that's what I'm doing :(

But the next thing I would do, is in /cljs/myapp/core.cljs - I would find a way to pull in this token from the meta tag (maybe by using vanilla javascript) and then add it to my post commands. 

A couple things that confused me:

It's not obvious that you have to add ring.middleware.anti-forgery to handler.clj, because in the examples in the documentation we're pulling that in so we can do "wrap-anti-forgery" in our middleware.clj. However, this is done automatically using ring.middleware.defaults, in the middleware.clj file, so it looks like ring.middleware.anti-forgery doesn't need to be anywhere. 

However, without ring.middleware.anti-forgery, it seems *anti-forgery-token* isn't bound.. once you require that, that it exists!

Let me know if my assumptions are wrong on this - it's possible I'm going down the wrong direction and that *anti-forgery-token* is actually stored in the session somewhere, but I have no idea how to access the session at this point.. That sounds like a whole nother bag of worms.

Epilogue

The turning point for me was realizing that the ring-defaults file is ridiculously easy to comprehend on Github. I'm so used to php projects where inspecting classes is basically impossible - in order to understand one thing, you need to understand 12 other things, which in turn require an understanding of 3 other things etc. To open up defaults.cli and see that site-defaults is just a simple map, literally made me sigh in relief :p

I'm beginning to grasp the structure of packages of namespaces, which makes seeing into the code easier.  It strikes me Clojure is a system which dramatically rewards investment into it's fundamental building blocks. I feel like I have a lot to learn, but it's all worth learning.  Thanks!

Reply all
Reply to author
Forward
0 new messages