Compojure app默认不发送CSRF

时间:2016-03-07 09:42:30

标签: clojure csrf compojure ring reagent

我使用试剂和compojure制作玩具网络应用程序而且我无法弄清楚为什么我的服务器没有发送CSRF cookie。其他答案和一些博客文章似乎暗示compojure的默认设置现在发送CSRF令牌,手动重新发送它实际上是一个错误。当我尝试点击POST /art端点时,我会收到403 Forbidden响应。所有页面都没有获得带有CSRF令牌的cookie,因此我无法通过POST请求发送它。有什么建议吗?

;;server.clj

(ns my-app.server
  (:require [my-app.handler :refer [app]]
            [environ.core :refer [env]]
            [ring.adapter.jetty :refer [run-jetty]])
  (:gen-class))

 (defn -main [& args]
   (let [port (Integer/parseInt (or (env :port) "3000"))]
     (run-jetty app {:port port :join? false})))

;; handler.clj

(ns my-app.handler
  (:require [compojure.core :refer [GET POST defroutes]]
            [compojure.route :refer [not-found resources]]
            [hiccup.page :refer [include-js include-css html5]]
            [my-app.middleware :refer [wrap-middleware]]
            [environ.core :refer [env]]))


(defroutes routes
  (GET "/" [] loading-page)
  (GET "/about" [] loading-page)
  (GET "/art" [] loading-page)
  (POST "/art" request {:sent (:body request) :hello "world"})

  (resources "/")
  (not-found "Not Found"))

(def app (wrap-middleware #'routes))


;;middleware.clj

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

(defn wrap-middleware [handler]
  (-> handler
      (wrap-defaults site-defaults)
      wrap-exceptions
      wrap-reload))

我把代码本身放在github here上,因为我仍然无法看到错误。

2 个答案:

答案 0 :(得分:2)

  

其他答案和几篇博文似乎暗示compojure的默认设置现在发送CSRF令牌,手动重新发送它实际上是一个错误。

(wrap-defaults site-defaults)应用ring-anti-forgery中间件。这只会为每个环浏览器会话添加一个CSRF令牌,并在POST请求中查找令牌。如果缺少令牌,则中间件将为请求返回403。将令牌添加到您的表单或ajax /任何发布请求取决于您,自由的代价。 :)

来自ring-anti-forgery文档:

  

默认情况下,令牌应位于名为“__anti-forgery-token”的表单字段中,或者位于“X-CSRF-Token”或“X-XSRF-Token”标题中。

例如,尝试添加此路线:

(GET "/someform" request (html5 (ring.util.anti-forgery/anti-forgery-field)))

anti-forgery-field帮助器将添加一个隐藏的输入字段,其中包含CSRF令牌作为其值,如果表单已过帐,则由中间件拾取。要直接访问令牌,您可以使用ring.middleware.anti-forgery/*anti-forgery-token*或在当前请求映射的会话中查找它:

(-> request :session :ring.middleware.anti-forgery/anti-forgery-token)

对于处理程序上下文,全局var(以及对帮助程序的扩展名)是bound,但是您无法从外部或从同一上下文中的另一个线程访问它。

简单的卷曲标题示例:

  1. 获取CSRF令牌:

    $ curl -v -c /tmp/cookiestore.txt http://localhost:3000/someform
    

  2. 通过标头设置令牌并发布一些内容:

    $ curl -v -b /tmp/cookiestore.txt --header "X-CSRF-Token: ->token from prev. req<-" -X POST -d '{:foo "bar"}' localhost:3000/art
    

答案 1 :(得分:1)

所以我找到了一个相关问题的答案,这个问题对我来说已经足够了。我将middleware.clj site-defaults切换到不使用CSRF的api-defaults。仍然很好奇如何让CSRF在这里工作,但这对我正在做的事情并不重要。如果后者有人建议修复有效,我会将其标记为正确。