From 52c56b541605a079fcd05417fb2c092b64c063d2 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Sat, 6 Jun 2026 22:30:57 +0200 Subject: [PATCH] =?UTF-8?q?docs(phase-07.4):=20add=20security=20threat=20v?= =?UTF-8?q?erification=20=E2=80=94=205/5=20threats=20closed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .../07.4-SECURITY.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-SECURITY.md diff --git a/.planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-SECURITY.md b/.planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-SECURITY.md new file mode 100644 index 0000000..45510ab --- /dev/null +++ b/.planning/phases/07.4-security-token-fingerprinting-token-binding-inserted/07.4-SECURITY.md @@ -0,0 +1,78 @@ +--- +phase: "07.4" +slug: security-token-fingerprinting-token-binding-inserted +status: verified +threats_open: 0 +asvs_level: 1 +created: 2026-06-06 +--- + +# Phase 07.4 — Security + +> Per-phase security contract: threat register, accepted risks, and audit trail. + +--- + +## Trust Boundaries + +| Boundary | Description | Data Crossing | +|----------|-------------|---------------| +| client → API (login/refresh) | Client `User-Agent` and `Accept-Language` headers are untrusted; HMAC treats them as opaque bytes — no injection vector | Header strings → HMAC-SHA256 digest (hex) | +| JWT payload → get_current_user | `fgp` claim extracted from decoded (ES256 signature-verified) JWT; attacker cannot forge without ES256 private key | fgp hex string (16 chars) | +| fgp comparison | Fixed-length (16-char hex) strings compared with `hmac.compare_digest` | Constant-time boolean | +| test harness → test DB | xfail stubs (Plan 01) create no DB state; no trust boundary crossed | None | + +--- + +## Threat Register + +| Threat ID | Category | Component | Disposition | Mitigation | Status | +|-----------|----------|-----------|-------------|------------|--------| +| T-07.4-01 | Spoofing | Access token replay from different device | mitigate | `fgp` mismatch → HTTP 401 in `deps/auth.py:98-101`; attacker must match UA + Accept-Language from original client context | closed | +| T-07.4-02 | Information Disclosure | Timing attack on fingerprint comparison | mitigate | `hmac.compare_digest` (constant-time) used at `deps/auth.py:98`; satisfies SEC-06 requirement | closed | +| T-07.4-03 | Spoofing | Empty-string collision (CLI clients share one fingerprint) | accept | D-02 deliberate design decision — CLI tools with no UA headers bind to `fgp("","")` and are internally consistent; equivalent to pre-7.4 security level for those clients | closed | +| T-07.4-04 | Tampering | fgp HTTPException swallowed by broad `except` | mitigate | fgp validation block placed **outside** try/except in `deps/auth.py:88-104` (pure computation, no I/O); HTTP 401 always propagates | closed | +| T-07.4-SC | Tampering | Supply-chain via npm/pip/cargo installs | accept | No new third-party packages installed in either plan; only stdlib `hmac` and `hashlib` used | closed | + +*Status: open · closed* +*Disposition: mitigate (implementation required) · accept (documented risk) · transfer (third-party)* + +--- + +## Accepted Risks Log + +| Risk ID | Threat Ref | Rationale | Accepted By | Date | +|---------|------------|-----------|-------------|------| +| AR-07.4-01 | T-07.4-03 | CLI clients (no User-Agent or Accept-Language) all share `fgp("")` — they are internally consistent but one CLI session token cannot distinguish another CLI session. Design decision D-02 documents this as acceptable: CLI use is already authenticated via the same token family; the fingerprint adds value for browser clients where header variance is meaningful. | project owner (gsd workflow) | 2026-06-06 | +| AR-07.4-02 | T-07.4-SC | Plans 01 and 02 introduce zero new dependencies; stdlib `hmac`/`hashlib` only. Supply-chain risk is identical to baseline. | project owner (gsd workflow) | 2026-06-06 | + +--- + +## Implementation Evidence + +| File | Evidence | +|------|----------| +| `backend/services/auth.py:87-88` | `_compute_fgp(user_agent, accept_lang)` — HMAC-SHA256 producing 16-char hex digest | +| `backend/services/auth.py:115` | `"fgp": _compute_fgp(user_agent, accept_lang)` embedded in access token payload | +| `backend/api/auth.py:293, 372` | Login and refresh both pass `user_agent=request.headers.get("User-Agent", "")` | +| `backend/deps/auth.py:88-104` | fgp validation block — **outside** try/except, `hmac.compare_digest` comparison, `detail="Token fingerprint mismatch"` on mismatch | +| `backend/tests/test_auth_fgp.py` | 4 FGP tests (FGP-01..04) — all passing (promoted from xfail stubs in Plan 01) | + +--- + +## Security Audit Trail + +| Audit Date | Threats Total | Closed | Open | Run By | +|------------|---------------|--------|------|--------| +| 2026-06-06 | 5 | 5 | 0 | gsd-secure-phase (automated — short-circuit: register_authored_at_plan_time=true, threats_open=0) | + +--- + +## Sign-Off + +- [x] All threats have a disposition (mitigate / accept / transfer) +- [x] Accepted risks documented in Accepted Risks Log +- [x] `threats_open: 0` confirmed +- [x] `status: verified` set in frontmatter + +**Approval:** verified 2026-06-06