-
-
Notifications
You must be signed in to change notification settings - Fork 194
6 CSRF and origin protection
Sente checks every connection request (Ajax POST, WebSocket/Ajax handshake) before accepting it. Configure the checks via make-channel-socket-server! options.
CSRF protection is strongly recommended for websites. The default token setup is in Getting started; this page covers the options.
Checks run in order; the first failure rejects the request:
| # | Check | Option | Default rejection |
|---|---|---|---|
| 1 | Custom (any reason) | :reject-fn |
your returned response |
| 2 | CSRF | :csrf-token-fn |
:bad-csrf-fn → 403 |
| 3 | Origin | :allowed-origins |
:bad-origin-fn → 403 |
| 4 | Authorization | :authorized?-fn |
:unauthorized-fn → 401 |
Each responder is a (fn [ring-req]) -> ring-resp you can override.
Sente compares a session reference token against the client's (the :csrf-token param, or x-csrf-token / x-xsrf-token header). Add anti-forgery middleware (ring-anti-forgery or ring-defaults) and pass the token to your client; see Getting started.
Disable it by returning :sente/skip-CSRF-check from :csrf-token-fn.
A same-origin request can't be cross-site, so checking Origin (with a Referer fallback, per OWASP) is a token-free CSRF defense. :allowed-origins is an always-on origin gate:
{:allowed-origins #{"https://example.com"}
:csrf-token-fn (constantly :sente/skip-CSRF-check)} ; Origin is the CSRF defenseBad origins reject via :bad-origin-fn.
One hook to reject a request for any reason: (fn [ring-req]) -> ?resp. Non-nil rejects with that response; nil falls through to the built-in checks. It runs first and can only add rejections.
Use it for CSRF the token check can't express (signed token, double-submit cookie, Fetch Metadata…) via the allow-origin? and valid-csrf-token? helpers, or for reasons Sente doesn't model (rate limits, IP/maintenance gates):
;; Accept a trusted origin OR a valid token (e.g. browser + API clients).
;; Disable the built-in token check, since we handle CSRF here:
{:csrf-token-fn (constantly :sente/skip-CSRF-check)
:reject-fn
(fn [req]
(when-not (or (sente/allow-origin? #{"https://example.com"} req)
(sente/valid-csrf-token? my-token-fn req))
{:status 403}))}
;; Rate-limit, block IPs, etc.:
{:reject-fn (fn [req] (when (rate-limited? req) {:status 429}))}Read the token from anywhere, e.g. the Sec-WebSocket-Protocol header (WebSockets can't set custom headers). Compare secret tokens in constant time (const-str=).
:reject-fnreplaces the deprecated:?unauthorized-fn.