我试图通过在标题中设置X-CSRF-Token 来实现Ring-Anti-Forgery库.
由于我使用的是静态html文件,因此我发现内置的打嗝帮助器(它在表单中设置令牌)是无用的.
这是我第一次尝试使用Clojure进行Web开发,所以我猜我完全错过了那些有经验的人应该明白的东西.
README状态的说明:
中间件还在X-CSRF-Token和X-XSRF-Token头字段中查找令牌.可以使用:read-token选项进一步自定义此行为:
(defn get-custom-token [request] (get-in request [:headers "x-forgery-token"])) (def app (-> handler (wrap-anti-forgery {:read-token get-custom-token}) (wrap-session)))
我已经将上面的内容添加到handler.clj而没有任何成功.
project.clj
(defproject hooktale "0.0.1" :description "Hooktale iOS App Website" :url "http://www.hooktale.com" :repositories {"sonartype releases" "https://oss.sonatype.org/content/repositories/releases/"} :source-paths ["src/clj" "src/cljs"] :dependencies [[org.clojure/clojure "1.5.1"] [org.clojure/clojurescript "0.0-2080"] [org.clojure/java.jdbc "0.3.0-beta2"] [compojure "1.1.6"] [com.mchange/c3p0 "0.9.5-pre5"] [org.postgresql/postgresql "9.3-1100-jdbc4"] [ring-anti-forgery "0.3.0"]] :plugins [[lein-ring "0.8.8"] [lein-cljsbuild "1.0.1-SNAPSHOT"]] :ring {:handler hooktale.handler/app} :profiles {:dev {:plugins [[javax.servlet/servlet-api "2.5"] [ring-mock "0.1.5"]] :cljsbuild {:builds [{:source-paths ["src/cljs"] :compiler {:optimizations :advanced :pretty-print false :output-to "resources/public/js/trout.js"}}]}}})
handler.clj
(ns hooktale.handler (:require [compojure.core :refer [defroutes GET POST]] [compojure.handler :refer [site]] [compojure.route :refer [resources not-found]] [clojure.java.io :refer [resource]] [ring.middleware.anti-forgery :refer :all] [ring.middleware.session :refer [wrap-session]] [hooktale.controllers.prospect :refer [create-prospect]])) (defn get-custom-token [request] (get-in request [:headers "x-forgery-token"])) (defroutes app-routes (GET "/" [] (resource "public/index.html")) (POST "/" [email] (create-prospect email)) (resources "/") (not-found "Not Found")) (def app (-> (site app-routes) (wrap-anti-forgery {:read-token get-custom-token}) (wrap-session)))
向页面发送请求将返回以下信息:
curl -I localhost:3000
HTTP/1.1 200 OK Date: Fri, 06 Dec 2013 16:30:45 GMT Set-Cookie: ring-session=0b2a477f-9352-4fd8-a3c3-a6b6f8d9e063;Path=/ Content-Length: 0 Server: Jetty(7.6.8.v20121106)
curl -X POST -d '{:email "piglet@aol.com"}' localhost:3000
Invalid anti-forgery token
我认为ring.middleware.anti-forgery中的函数允许我在标题中设置标记而不必在表单字段中设置隐藏标记值.
(defn- default-request-token [request] (or (-> request form-params (get "__anti-forgery-token")) (-> request :headers (get "x-csrf-token")) (-> request :headers (get "x-xsrf-token"))))
如果我正确读取它,它将检查表单中的令牌,如果没有,它将检查x-csrf-token,然后检查头中的x-xsrf-token.
我似乎很难在标题中实际设置x-csrf-token或x-xsrf-token的值.
卷曲反应
查看ring-session设置的Cookie:
curl -I localhost:3000
HTTP/1.1 200 OK Date: Fri, 06 Dec 2013 19:52:22 GMT Set-Cookie: ring-session=b02dd6f8-74b8-4ce0-a1d6-07251dadb9aa;Path=/ Content-Length: 0 Server: Jetty(7.6.8.v20121106)
设置X-CSRF-Token:
curl -v --header "X-CSRF-Token: b02dd6f8-74b8-4ce0-a1d6-07251dadb9aa;Path=/" -X POST -d '{:email "starbuck@bsg.com"}' localhost:3000
* Adding handle: conn: 0x7fd3ab004000 * Adding handle: send: 0 * Adding handle: recv: 0 * Curl_addHandleToPipeline: length: 1 * - Conn 0 (0x7fd3ab004000) send_pipe: 1, recv_pipe: 0 * About to connect() to localhost port 3000 (#0) * Trying ::1... * Connected to localhost (::1) port 3000 (#0) > POST / HTTP/1.1 > User-Agent: curl/7.30.0 > Host: localhost:3000 > Accept: */* > X-CSRF-Token: b02dd6f8-74b8-4ce0-a1d6-07251dadb9aa;Path=/ > Content-Length: 27 > Content-Type: application/x-www-form-urlencoded > * upload completely sent off: 27 out of 27 bytes < HTTP/1.1 403 Forbidden < Date: Fri, 06 Dec 2013 19:54:52 GMT < Content-Type: text/html;charset=ISO-8859-1 < Content-Length: 35 * Server Jetty(7.6.8.v20121106) is not blacklisted < Server: Jetty(7.6.8.v20121106) < * Connection #0 to host localhost left intactInvalid anti-forgery token
edbond.. 11
我用示例创建了一个存储库https://github.com/edbond/CSRF.自述文件描述了使用CSRF令牌POST请求所需的过程.
简而言之(对于API调用,curl):
从服务器获取CSRF令牌和会话cookie(服务器将CSRF令牌存储在您的会话中,由cookie标识)
发送X-CSRF-Token和cookie以及POST请求(服务器将CSRF令牌与存储在您的会话中的cookie进行比较)
cookie - > session - > CSRF-Token
对于HTML,表单POSTing它应该足以包括(反伪造字段)表单.注意,您也可以使用curl发送表单字段而不是标题.
HTH