设置Ring-Anti-Forgery CSRF标头令牌

时间:2013-12-06 17:46:24

标签: clojure header csrf middleware ring

我正在尝试通过在标头中设置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

<h1>Invalid anti-forgery token</h1>

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 intact
<h1>Invalid anti-forgery token</h1>

1 个答案:

答案 0 :(得分:11)

我用示例创建了一个存储库https://github.com/edbond/CSRF。自述文件描述了使用CSRF令牌POST请求所需的过程。

简而言之(对于API调用,curl):

  1. 从服务器获取CSRF令牌和会话cookie(服务器将CSRF令牌存储在您通过cookie识别的会话中)

  2. 发送X-CSRF-Token和cookie以及POST请求(服务器会将CSRF令牌与存储在您的会话中的cookie进行比较)

  3. cookie - &gt;会话 - &gt; CSRF令牌

    对于HTML,表单POST,它应该足以包含(反伪造字段)表单。请注意,您还可以使用curl发送表单字段而不是标题。

    HTH