docs(phase-07.4): add security threat verification — 5/5 threats closed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3414571091
commit
52c56b5416
+78
@@ -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
|
||||
Reference in New Issue
Block a user