dbsc-toolkit 2.9.1 → 2.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +68 -34
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -7,18 +7,48 @@
7
7
 
8
8
  > **Stop stolen session cookies from being replayed on another device.**
9
9
  >
10
- > `dbsc-toolkit` implements W3C [Device Bound Session Credentials](https://www.w3.org/TR/dbsc/) on the server, and ships a Web Crypto polyfill so the same protection works on browsers that don't speak native DBSC yet. Native DBSC on Chromium 146+ Windows; the polyfill on every other browser. **One server, every browser, no user prompts.**
10
+ > `dbsc-toolkit` implements W3C [Device Bound Session Credentials](https://www.w3.org/TR/dbsc/) on the server, and ships a Web Crypto polyfill so the same protection works on browsers that don't speak native DBSC yet. Native DBSC on Chromium 146+ (TPM / Secure Enclave); the polyfill on every other browser. **One server, every browser, no user prompts.**
11
11
  >
12
12
  > Works with **Express · Fastify · Hono · Next.js · Redis · PostgreSQL**.
13
13
 
14
+ ```ts
15
+ // In your login route — one line to bind the session:
16
+ app.post("/login", async (req, res) => {
17
+ await dbsc.bind(res, sessionId, { userId });
18
+ res.json({ ok: true });
19
+ });
20
+
21
+ // On any sensitive route — one guard:
22
+ app.post("/payment", express.raw({ type: "*/*" }), requireProof(), paymentHandler);
23
+ ```
24
+
25
+ That's it. The rest — key generation, protocol routes, cookie management, polyfill — is handled by the library.
26
+
27
+ ## Contents
28
+
29
+ - [Install](#install)
30
+ - [Quick start](#quick-start)
31
+ - [Live demo](#live-demo)
32
+ - [Why dbsc-toolkit](#why-dbsc-toolkit)
33
+ - [Comparison](#comparison)
34
+ - [Why not JWT?](#why-not-just-use-jwt)
35
+ - [How it works](#how-it-works-the-30-second-version)
36
+ - [Who is this for?](#who-is-this-for)
37
+ - [Adding to an existing app](#adding-to-an-existing-app)
38
+ - [Protect your routes](#protect-your-routes)
39
+ - [Production checklist](#production-checklist)
40
+ - [API](#subpath-imports)
41
+ - [Security model](#security-model)
42
+ - [Going deeper](#going-deeper)
43
+
14
44
  ## Why dbsc-toolkit
15
45
 
16
46
  Native DBSC ships on Chrome 146+ for Windows users with a TPM. That's about a third of internet users. Every other DBSC implementation gives the rest of your users nothing — they remain on plain bearer cookies.
17
47
 
18
- This library is the **only** DBSC implementation that covers the other two-thirds via a Web Crypto polyfill. Same wire-level protection against cookie theft, same `requireProof()` guard server-side, key stored non-extractably in IndexedDB instead of a TPM. No biometric prompt, no user interaction, no checkbox.
48
+ This library is among the first DBSC implementations to provide a Web Crypto fallback for browsers that don't support native DBSC. Same wire-level protection against cookie theft, same `requireProof()` guard server-side, key stored non-extractably in IndexedDB instead of a TPM. No biometric prompt, no user interaction, no checkbox.
19
49
 
20
- - ✅ **Native W3C DBSC** on Chromium 146+ (TPM / Secure Enclave / Android Keystore)
21
- - ✅ **Web Crypto polyfill** on Firefox, Safari, older Chromium — same protection, IndexedDB key
50
+ - ✅ **Native W3C DBSC** on Chromium 146+ (TPM on Windows, Secure Enclave on macOS)
51
+ - ✅ **Web Crypto polyfill** on Firefox, Safari, mobile, older Chromium — same protection, IndexedDB key
22
52
  - ✅ **Express, Fastify, Hono, Next.js** adapters — all four ship in one package
23
53
  - ✅ **Memory, Redis, Postgres** storage — pick one, swap with one line
24
54
  - ✅ **`requireProof()`** per-request signature guard — works on every browser, body-hash defended against MITM
@@ -110,42 +140,19 @@ The native-DBSC column refers to a server that uses the spec without a polyfill
110
140
 
111
141
  JWTs are still bearer tokens. Whoever has the bytes can replay them. Steal a JWT from a log line, an XSS, a leaked proxy header, a misconfigured intermediary — paste it into another machine and the server treats it as the legitimate user, exactly like a stolen cookie.
112
142
 
113
- DBSC pins the session to a cryptographic key that lives on the user's device (TPM, Secure Enclave, Android Keystore, or — under the polyfill — a non-extractable IndexedDB key). The token alone is no longer sufficient: the browser has to prove possession of the key on every refresh, and on every guarded request via `requireProof()`. The attacker's device has no key, so the replay fails and the session demotes within one cycle.
143
+ DBSC pins the session to a cryptographic key that lives on the user's device (TPM on Windows, Secure Enclave on macOS, or — under the polyfill — a non-extractable IndexedDB key on every other browser). The token alone is no longer sufficient: the browser has to prove possession of the key on every refresh, and on every guarded request via `requireProof()`. The attacker's device has no key, so the replay fails and the session demotes within one cycle.
114
144
 
115
145
  DBSC complements your existing auth (passwords, MFA, JWT/session cookies) — it does not replace them. It closes the *replay-after-issue* gap that bearer tokens cannot.
116
146
 
117
147
  ## How it works (the 30-second version)
118
148
 
119
149
  ```
120
- Browser Server
121
- ─────── ──────
122
- │ POST /login (username, password) │
123
- │ ───────────────────────────────────────────────────▶ │
124
- │ │ verify password
125
- │ 200 + Secure-Session-Registration header │
126
- │ ◀─────────────────────────────────────────────────── │
127
- │ │
128
- │ generate keypair │
129
- │ (TPM / Secure Enclave / IndexedDB) │
130
- │ │
131
- │ POST /dbsc/registration │
132
- │ Secure-Session-Response: <JWS containing pub key> │
133
- │ ───────────────────────────────────────────────────▶ │
134
- │ │ verify self-signature
135
- │ │ store sessionId → pubkey
136
- │ 200 + __Host-dbsc-session cookie (Max-Age 10 min) │
137
- │ ◀─────────────────────────────────────────────────── │
138
- │ │
139
- │ every ~10 min, browser hits /dbsc/refresh │
140
- │ signing a fresh server challenge with the key │
141
- │ ───────────────────────────────────────────────────▶ │
142
- │ │ verify signature
143
- │ │ reissue cookie
144
- │ 200 + fresh __Host-dbsc-session │
145
- │ ◀─────────────────────────────────────────────────── │
150
+ Login → Key Registration → Cookie Issued → Challenge → Signature Verification → Session Refresh
146
151
  ```
147
152
 
148
- A copy of `__Host-dbsc-session` pasted into another browser has no matching key — the next refresh fails, the session demotes to `tier: "none"`, and `requireProof()` 403s every guarded request immediately (not on the next refresh cycle). Full walk-through with the exact wire format: [HOW-IT-WORKS.md](./HOW-IT-WORKS.md).
153
+ On login the server responds with a `Secure-Session-Registration` header. The browser generates an ECDSA keypair in the TPM (Chrome) or IndexedDB (Firefox/Safari/polyfill), POSTs the public key, and gets back a short-lived session cookie. Every ~10 minutes the browser re-signs a fresh server challenge to renew the cookie. A copy of the cookie pasted into another device has no key — the next refresh fails and `requireProof()` 403s every guarded request immediately.
154
+
155
+ Full wire-format walk-through: [HOW-IT-WORKS.md](./HOW-IT-WORKS.md).
149
156
 
150
157
  ## Who is this for?
151
158
 
@@ -220,6 +227,16 @@ For apps with a stricter threat model (active MITM, log-spillage exposure), v2.8
220
227
 
221
228
  The full threat boundary, the per-framework wiring (Fastify / Hono / Next.js), and the migration timeline for an existing app are in [docs/integrating-existing-auth.md](./docs/integrating-existing-auth.md) and [docs/per-request-signing.md](./docs/per-request-signing.md).
222
229
 
230
+ ## Production checklist
231
+
232
+ - [ ] HTTPS enabled — DBSC requires `Secure` cookies; plain HTTP locks every user to `tier: "none"`
233
+ - [ ] Redis or PostgreSQL storage — `MemoryStorage` is lost on restart; bound browsers loop registration
234
+ - [ ] `requireProof()` on every authenticated route — read-only public routes need no guard
235
+ - [ ] `wrapFetch({ signBody: true })` on the client side for those routes (already the default in v2.8+)
236
+ - [ ] Replay cache enabled — `replayCache: new RedisReplayCache(redis)` in `createDbsc({...})`
237
+ - [ ] `trust proxy` already set — `install()` sets it, but verify if you have your own app config overriding it
238
+ - [ ] Polyfill script loaded — the `<script type="module">` tag; without it Firefox/Safari stay on `tier: "none"`
239
+
223
240
  ## Subpath imports
224
241
 
225
242
  | Import | What it is |
@@ -240,7 +257,7 @@ Tree-shaking eliminates anything you don't import. Using Koa, Hapi, raw `http`,
240
257
 
241
258
  | Tier | Mechanism | Protects against |
242
259
  |------|-----------|------------------|
243
- | `dbsc` | Native W3C DBSC, key in TPM / Secure Enclave / Android Keystore | Cookie theft (XSS, network, logs, paste-to-other-browser) **and** infostealer malware reading the browser profile |
260
+ | `dbsc` | Native W3C DBSC, key in TPM (Windows) or Secure Enclave (macOS) | Cookie theft (XSS, network, logs, paste-to-other-browser) **and** infostealer malware reading the browser profile |
244
261
  | `bound` | Web Crypto polyfill, non-extractable ECDSA P-256 key in IndexedDB | Cookie theft. Does not defeat infostealer malware on the user's machine. |
245
262
  | `none` | No active / fresh binding | Nothing the cookie itself doesn't already do |
246
263
 
@@ -251,6 +268,23 @@ Tree-shaking eliminates anything you don't import. Using Koa, Hapi, raw `http`,
251
268
 
252
269
  There is no third option, and **never gate a route on `tier === "dbsc"`** — that locks out every Firefox and Safari user (they can only reach `tier: "bound"`), and `requireProof()` already gives those browsers the same per-request guarantee via a signed proof. `requireProof()` is the one guard; it works on every browser. Read `res.locals.dbsc.tier` only if you want to *display* binding state in the UI — not to build a gate. Full guidance in [docs/security/best-practices.md](./docs/security/best-practices.md).
253
270
 
271
+ ## Security model
272
+
273
+ `dbsc-toolkit` protects against:
274
+
275
+ - Stolen session cookies replayed from another device
276
+ - Stolen bearer tokens (same category — bearer == portable)
277
+ - Cookie exfiltration via XSS, network capture, log leakage, or proxy leakage
278
+
279
+ The Web Crypto polyfill (`tier: "bound"`) does **not** protect against:
280
+
281
+ - Malware already running on the user's machine with access to the browser profile
282
+ - Browser or OS compromise
283
+
284
+ Native DBSC (`tier: "dbsc"`) provides stronger protection against local malware because the private key lives inside the TPM or Secure Enclave — software cannot extract it even with admin access. The polyfill key is non-extractable to JavaScript, but the encrypted blob lives on disk and a sufficiently privileged attacker can reach it.
285
+
286
+ Neither tier is a substitute for HTTPS, input validation, strong passwords, or MFA.
287
+
254
288
  ## Going deeper
255
289
 
256
290
  - **Concepts and protocol:** [HOW-IT-WORKS.md](./HOW-IT-WORKS.md)
@@ -267,7 +301,7 @@ There is no third option, and **never gate a route on `tier === "dbsc"`** — th
267
301
 
268
302
  ## Status
269
303
 
270
- Verified end-to-end on Chrome 147 / Windows / TPM 2.0. Native DBSC supported on Chromium 145+ across Windows, macOS Apple Silicon, and Android. The bound polyfill works on every browser with Web Crypto + IndexedDB (Firefox, Safari, older Chromium). No third-party security audit yet. Production-readiness table and adoption guidance: [HOW-IT-WORKS.md#production-readiness](./HOW-IT-WORKS.md#production-readiness).
304
+ Verified end-to-end on Chrome 147 / Windows / TPM 2.0. Native DBSC requires Chromium 145+ on Windows or macOS Apple Silicon. The bound polyfill works on every browser with Web Crypto + IndexedDB (Firefox, Safari, mobile browsers, older Chromium). No third-party security audit yet. Production-readiness table and adoption guidance: [HOW-IT-WORKS.md#production-readiness](./HOW-IT-WORKS.md#production-readiness).
271
305
 
272
306
  ## License
273
307
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbsc-toolkit",
3
- "version": "2.9.1",
3
+ "version": "2.9.3",
4
4
  "description": "Server-side Device Bound Session Credentials (DBSC) for Node.js plus a Web Crypto polyfill for Firefox, Safari, and older Chromium. Cookie-theft protection on every modern browser.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",