Compojure 应用程序默认不发送 CSRF
Compojure app not sending CSRF by default
我正在使用 reagent 和 compojure 制作一个玩具网络应用程序,但我不明白为什么我的服务器不发送 CSRF cookie。其他答案和几篇博客文章似乎暗示 compojure 的默认设置现在发送 CSRF 令牌,而手动重新发送它实际上是一个错误。当我尝试访问 POST /art
端点时,我得到了 403 Forbidden 响应。 None 的页面获取其中包含 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 上,因为我仍然看不出有什么问题。
所以我找到了一个相关问题的答案,目前对我来说已经足够好了。我将 middleware.clj site-defaults
切换为不使用 CSRF 的 api-defaults
。仍然好奇如何让 CSRF 在这里工作,但这对我正在做的事情并不重要。如果后来有人提出有效的修复方法,我会将其标记为正确。
Other answers and several blog posts seem to imply that the default settings for compojure now send the CSRF token and that manually resending it is actually a bug.
(wrap-defaults site-defaults)
应用 ring-anti-forgery 中间件。这只会向每个环形浏览器 session 添加一个 CSRF 令牌,并在 POST 请求中查找令牌。如果缺少令牌,中间件将为请求 return 一个 403
。将令牌添加到您的表单或 ajax/whatever post 请求由您决定,这是自由的代价。 :)
来自 ring-anti-forgery 文档:
By default, the token is expected to be in a form field named '__anti-forgery-token', or in the 'X-CSRF-Token' or 'X-XSRF-Token' headers.
例如尝试添加这条路线:
(GET "/someform" request (html5 (ring.util.anti-forgery/anti-forgery-field)))
anti-forgery-field
帮助程序将添加一个隐藏的输入字段,其中包含 CSRF 令牌作为其值,如果表单是 posted,中间件将拾取该输入字段。要直接访问令牌,您可以使用 ring.middleware.anti-forgery/*anti-forgery-token*
或在当前请求映射的 session 中查找它:
(-> request :session :ring.middleware.anti-forgery/anti-forgery-token)
虽然全局变量(以及扩展助手)是 bound 处理程序上下文,但您不能从外部或同一上下文中的另一个线程访问它。
简单卷曲header示例:
获取 CSRF 令牌:
$ curl -v -c /tmp/cookiestore.txt http://localhost:3000/someform
通过 header 和 post 设置令牌:
$ curl -v -b /tmp/cookiestore.txt --header "X-CSRF-Token: ->token from prev. req<-" -X POST -d '{:foo "bar"}' localhost:3000/art
我正在使用 reagent 和 compojure 制作一个玩具网络应用程序,但我不明白为什么我的服务器不发送 CSRF cookie。其他答案和几篇博客文章似乎暗示 compojure 的默认设置现在发送 CSRF 令牌,而手动重新发送它实际上是一个错误。当我尝试访问 POST /art
端点时,我得到了 403 Forbidden 响应。 None 的页面获取其中包含 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 上,因为我仍然看不出有什么问题。
所以我找到了一个相关问题的答案,目前对我来说已经足够好了。我将 middleware.clj site-defaults
切换为不使用 CSRF 的 api-defaults
。仍然好奇如何让 CSRF 在这里工作,但这对我正在做的事情并不重要。如果后来有人提出有效的修复方法,我会将其标记为正确。
Other answers and several blog posts seem to imply that the default settings for compojure now send the CSRF token and that manually resending it is actually a bug.
(wrap-defaults site-defaults)
应用 ring-anti-forgery 中间件。这只会向每个环形浏览器 session 添加一个 CSRF 令牌,并在 POST 请求中查找令牌。如果缺少令牌,中间件将为请求 return 一个 403
。将令牌添加到您的表单或 ajax/whatever post 请求由您决定,这是自由的代价。 :)
来自 ring-anti-forgery 文档:
By default, the token is expected to be in a form field named '__anti-forgery-token', or in the 'X-CSRF-Token' or 'X-XSRF-Token' headers.
例如尝试添加这条路线:
(GET "/someform" request (html5 (ring.util.anti-forgery/anti-forgery-field)))
anti-forgery-field
帮助程序将添加一个隐藏的输入字段,其中包含 CSRF 令牌作为其值,如果表单是 posted,中间件将拾取该输入字段。要直接访问令牌,您可以使用 ring.middleware.anti-forgery/*anti-forgery-token*
或在当前请求映射的 session 中查找它:
(-> request :session :ring.middleware.anti-forgery/anti-forgery-token)
虽然全局变量(以及扩展助手)是 bound 处理程序上下文,但您不能从外部或同一上下文中的另一个线程访问它。
简单卷曲header示例:
获取 CSRF 令牌:
$ curl -v -c /tmp/cookiestore.txt http://localhost:3000/someform
通过 header 和 post 设置令牌:
$ curl -v -b /tmp/cookiestore.txt --header "X-CSRF-Token: ->token from prev. req<-" -X POST -d '{:foo "bar"}' localhost:3000/art