Skip to content

Commit dd6982f

Browse files
committed
[#137] NB SECURITY FIX, BREAKING: fix broken CSRF protection (@danielcompton, @awkay, @eerohele)
A pair of CRITICAL security issues were identified by contributors: 1. Sente was leaking its CSRF token from its WebSocket handshake route. And since in the common case, this is a shared token also used by the rest of the application, this means that Sente was often in practice leaking the application's CSRF token. 2. No CSRF protection was being provided for WebSocket handshakes. This commit makes the following changes- 1. [BREAKING] The client-side :chsk/handshake event now always has `nil` where it once provided the csrf-token provided by the server. I.e. before: `[:chsk/handshake [<?uid> <csrf-token> <?handshake-data> <first-handshake?>]] after: `[:chsk/handshake [<?uid> nil <?handshake-data> <first-handshake?>]] 2. [BREAKING] `make-channel-socket-client!` now takes an extra argment: an explicit csrf-token. The value for the token should be extracted from the page HTML (see example project). 3. CSRF *checks* are now performed by Sente directly, and don't depend on an external route wrapper like `ring-anti-forgery`, etc. 4. CSRF checks now cover all Sente's internal endpoints, including Ajax POSTs, long-polling requests, and WebSocket handshakes. 5. Sente will now by default fail to work without CSRF tokens properly configured.
1 parent 51e8888 commit dd6982f

File tree

5 files changed

+131
-75
lines changed

5 files changed

+131
-75
lines changed

example-project/project.clj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(defproject com.taoensso.examples/sente "1.13.0"
1+
(defproject com.taoensso.examples/sente "1.14.0-SNAPSHOT"
22
:description "Sente, reference web-app example project"
33
:url "https://github.com/ptaoussanis/sente"
44
:license {:name "Eclipse Public License"
@@ -15,7 +15,7 @@
1515
[org.clojure/core.async "0.4.490"]
1616
[org.clojure/tools.nrepl "0.2.13"] ; Optional, for Cider
1717

18-
[com.taoensso/sente "1.13.1"] ; <--- Sente
18+
[com.taoensso/sente "1.14.0-SNAPSHOT"] ; <--- Sente
1919
[com.taoensso/timbre "4.10.0"]
2020

2121
;;; TODO Choose (uncomment) a supported web server -----------------------
@@ -25,16 +25,16 @@
2525
;; [aleph "0.4.1"]
2626
;; -----------------------------------------------------------------------
2727

28-
[ring "1.6.3"]
28+
[ring "1.7.1"]
2929
[ring/ring-defaults "0.3.2"] ; Includes `ring-anti-forgery`, etc.
30-
;; [ring-anti-forgery "1.0.0"]
30+
;; [ring-anti-forgery "1.3.0"]
3131

3232
[compojure "1.6.1"] ; Or routing lib of your choice
3333
[hiccup "1.0.5"] ; Optional, just for HTML
3434

3535
;;; Transit deps optional; may be used to aid perf. of larger data payloads
3636
;;; (see reference example for details):
37-
[com.cognitect/transit-clj "0.8.309"]
37+
[com.cognitect/transit-clj "0.8.313"]
3838
[com.cognitect/transit-cljs "0.8.256"]]
3939

4040
:plugins

example-project/src/example/client.cljs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@
3030

3131
;;;; Define our Sente channel socket (chsk) client
3232

33+
(def ?csrf-token
34+
(when-let [el (.getElementById js/document "sente-csrf-token")]
35+
(.getAttribute el "data-csrf-token")))
36+
37+
(if ?csrf-token
38+
(->output! "CSRF token detected in HTML, great!")
39+
(->output! "CSRF token NOT detected in HTML, default Sente config will reject requests"))
40+
3341
(let [;; For this example, select a random protocol:
3442
rand-chsk-type (if (>= (rand) 0.5) :ajax :auto)
3543
_ (->output! "Randomly selected chsk type: %s" rand-chsk-type)
@@ -41,6 +49,7 @@
4149
{:keys [chsk ch-recv send-fn state]}
4250
(sente/make-channel-socket-client!
4351
"/chsk" ; Must match server Ring routing URL
52+
?csrf-token
4453
{:type rand-chsk-type
4554
:packer packer})]
4655

example-project/src/example/server.clj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
(:require
66
[clojure.string :as str]
77
[ring.middleware.defaults]
8+
[ring.middleware.anti-forgery :as anti-forgery]
89
[compojure.core :as comp :refer (defroutes GET POST)]
910
[compojure.route :as route]
1011
[hiccup.core :as hiccup]
@@ -65,6 +66,7 @@
6566
(defn landing-pg-handler [ring-req]
6667
(hiccup/html
6768
[:h1 "Sente reference example"]
69+
[:div#sente-csrf-token {:data-csrf-token anti-forgery/*anti-forgery-token*}]
6870
[:p "An Ajax/WebSocket" [:strong " (random choice!)"] " has been configured for this example"]
6971
[:hr]
7072
[:p [:strong "Step 1: "] " try hitting the buttons:"]
@@ -116,7 +118,10 @@
116118
"**NB**: Sente requires the Ring `wrap-params` + `wrap-keyword-params`
117119
middleware to work. These are included with
118120
`ring.middleware.defaults/wrap-defaults` - but you'll need to ensure
119-
that they're included yourself if you're not using `wrap-defaults`."
121+
that they're included yourself if you're not using `wrap-defaults`.
122+
123+
You're also STRONGLY recommended to use `ring.middleware.anti-forgery`
124+
or something similar."
120125
(ring.middleware.defaults/wrap-defaults
121126
ring-routes ring.middleware.defaults/site-defaults))
122127

project.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(defproject com.taoensso/sente "1.13.1"
1+
(defproject com.taoensso/sente "1.14.0-SNAPSHOT"
22
:author "Peter Taoussanis <https://www.taoensso.com>"
33
:description "Realtime web comms for Clojure/Script"
44
:url "https://github.com/ptaoussanis/sente"
@@ -13,7 +13,7 @@
1313
:dependencies
1414
[[org.clojure/clojure "1.9.0"]
1515
[org.clojure/core.async "0.4.490"]
16-
[com.taoensso/encore "2.102.0"]
16+
[com.taoensso/encore "2.105.0"]
1717
[org.clojure/tools.reader "1.3.2"]
1818
[com.taoensso/timbre "4.10.0"]]
1919

0 commit comments

Comments
 (0)