设置 Ring-Anti-Forgery CSRF 标头令牌 [英] Set Ring-Anti-Forgery CSRF header token
问题描述
我正在尝试通过设置 X-CSRF 来实现 Ring-Anti-Forgery 库- 标头中的令牌.
I'm attempting to implement the Ring-Anti-Forgery library via setting the X-CSRF-Token in the header.
因为我使用的是静态 html 文件,所以我发现内置的 hiccup 助手(它在表单中设置令牌)没有用.
Since I am using static html files I found the built-in hiccup helper, which sets the token in the form, to be useless.
这是我第一次尝试使用 Clojure 进行 Web 开发,所以我猜我完全错过了对有经验的人来说应该显而易见的东西.
来自README状态的说明:
The instructions from the README state:
中间件还会在 X-CSRF-Token 中查找令牌并X-XSRF-Token 头域.这种行为可以进一步定制使用 :read-token 选项:
The middleware also looks for the token in the X-CSRF-Token and X-XSRF-Token header fields. This behavior can be customized further using the :read-token option:
(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 中,但没有成功.
I have added the above to handler.clj without any success.
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)))
向页面发送请求返回以下信息:
Sending a request to the page returns the following info:
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 中的函数可以让我在标头中设置令牌,而不必在表单字段中设置隐藏的令牌值.
The function in ring.middleware.anti-forgery that I thought would allow me to set the token in the header without having to set the hidden token value inside the form field.
(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.
If I am reading it correctly, it will check for the token in the form, if not there it will check for the x-csrf-token then the x-xsrf-token in the header.
我似乎很难在标题中实际设置 x-csrf-token 或 x-xsrf-token 的值.
I seem to be having difficulty in actually setting the value of x-csrf-token or x-xsrf-token in the header.
卷曲响应
查看ring-session设置的Cookie:
View the Cookie set by ring-session:
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:
Setting the X-CSRF-Token:
curl -v --header "X-CSRF-Token: b02dd6f8-74b8-4ce0-a1d6-07251dadb9aa;Path=/" -X POST -d '{:email "starbuck@bsg.com"}' 本地主机: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>
推荐答案
我创建了一个仓库 https://github.com/edbond/CSRF 举例.自述文件描述了使用 CSRF 令牌 POST 请求所需的过程.
I created a repository https://github.com/edbond/CSRF with example. Readme describes process needed to POST requests with CSRF token.
简而言之(对于 API 调用,curl):
In short (for API calls, curl):
从服务器获取 CSRF 令牌和会话 cookie(服务器会将 CSRF 令牌存储在您的会话中,由 cookie 标识)
Get CSRF Token and session cookie from server (server will store CSRF token inside your session which identified by cookie)
将 X-CSRF-Token 和 cookie 与 POST 请求一起发送(服务器会将 CSRF 令牌与存储在由 cookie 标识的会话中的令牌进行比较)
Send X-CSRF-Token and cookie along with POST request (server will compare CSRF token with that stored inside your session identified by cookie)
cookie -> session -> CSRF-Token
cookie -> session -> CSRF-Token
对于 HTML,表单 POST 应该足以将 (anti-forgery-field) 包含到表单中.请注意,您还可以使用 curl 发送表单字段而不是标题.
For HTML, form POSTing it should be enough to include (anti-forgery-field) to forms. Note, you can also send form field instead of header using curl.
HTH
这篇关于设置 Ring-Anti-Forgery CSRF 标头令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!