dream: signed session cookies (tamper-evident sid) + 11 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 37s

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 15:10:03 +00:00
parent b061442c06
commit 7d2d8478cc
3 changed files with 127 additions and 11 deletions

View File

@@ -72,6 +72,48 @@
dream-drop-cookie
(fn (resp name) (dream-set-cookie resp name "" {:max-age 0})))
;; ── signed cookie values (tamper-evident) ──────────────────────────
;; NOTE: pure-SX keyed hash — not cryptographic; production should inject a host
;; HMAC. Value carries no "." so the first "." splits value from signature.
(define
dr/sess-hash
(fn (s) (dr/sess-hash-loop s 0 (string-length s) 7)))
(define
dr/sess-hash-loop
(fn
(s i n h)
(if
(>= i n)
h
(dr/sess-hash-loop
s
(+ i 1)
n
(mod (+ (* h 131) (char-code (char-at s i))) 2147483647)))))
(define
dr/sess-sig
(fn (secret val) (str (dr/sess-hash (str secret "|" val)))))
(define
dream-cookie-sign
(fn (secret val) (str val "." (dr/sess-sig secret val))))
(define
dream-cookie-unsign
(fn
(secret signed)
(if
(or (nil? signed) (= signed ""))
nil
(let
((dot (index-of signed ".")))
(if
(< dot 0)
nil
(let
((val (substr signed 0 dot))
(sig (substr signed (+ dot 1))))
(if (= sig (dr/sess-sig secret val)) val nil)))))))
;; ── in-memory session store (tests + demos) ────────────────────────
;; A backend is (fn (op) result) where op is a dict {:op ... :sid ... :key ...}.
(define
@@ -143,6 +185,32 @@
sid
{:path "/" :http-only true :same-site "Lax"}))))))))))
;; signed variant: the cookie value is signed so a guessed/forged sid is rejected
(define
dream-sessions-signed
(fn
(backend secret)
(fn
(next)
(fn
(req)
(let
((sid0 (dream-cookie-unsign secret (dream-cookie req dream-session-cookie-name))))
(let
((have (and sid0 (backend {:op "session/exists" :sid sid0}))))
(let
((sid (if have sid0 (backend {:op "session/create"}))))
(let
((resp (next (assoc req :dream-session {:io backend :sid sid}))))
(if
have
resp
(dream-set-cookie
resp
dream-session-cookie-name
(dream-cookie-sign secret sid)
{:path "/" :http-only true :same-site "Lax"}))))))))))
;; ── handler-facing session API ─────────────────────────────────────
(define dr/session-of (fn (req) (get req :dream-session)))
(define dream-session-id (fn (req) (get (dr/session-of req) :sid)))