docs(phase-19.4): add security threat verification

This commit is contained in:
2026-10-20 00:38:40 +02:00
parent 8c202a25fe
commit ba5f7a6d1f

View File

@@ -0,0 +1,116 @@
---
phase: "19.4"
slug: "oidc-provider-on-wristband"
status: verified
# threats_open = count of OPEN threats at or above workflow.security_block_on severity (the blocking gate)
threats_open: 0
asvs_level: 1
created: "2026-10-20"
---
# Phase 19.4 — Security
> Per-phase security contract: threat register, accepted risks, and audit trail. The evidence (file and line, pinning test) for every threat is in `19.4-SECURITY-REVIEW.md`.
---
## Trust Boundaries
| Boundary | Description | Data Crossing |
|----------|-------------|---------------|
| browser to authorize, resume, decide, switch-account | untrusted query and form input, a session cookie that may be stale or someone else's | session JWT, consent token (high) |
| relying party to token endpoint | client credentials, codes and verifiers from a server on the internet | client secret, code, PKCE verifier (high) |
| bearer holder to userinfo | any presented token string | access token, identity claims (high) |
| engine to host and storage | the host renders HTML and resolves the user; storage returns rows the engine trusts | user identity, client rows (medium) |
| engine to relying party | signed id_tokens and JWKS the relying party trusts for identity | id_token, public keys (high) |
| administrator to admin screens | clients, branding, secrets behind `manage_clients` and `manage_settings` | client secret shown once, branding CSS (high) |
| WinterCMS export to import command | key files and rows from the PHP site | private keys, secret hashes (high) |
| test results to the phase verdict | the gate's output decides whether the phase is verified | test output (medium) |
---
## Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation | Status |
|-----------|----------|-----------|----------|-------------|------------|--------|
| T-19.4-01 | Spoofing | oidcAuthorize, matchOIDCRedirectURI | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-02 | Spoofing | authorize and token PKCE | high | mitigate | 19.4-SECURITY-REVIEW.md: fixed in commit eaa545c (test pin; code unchanged) | closed |
| T-19.4-03 | Tampering | oidcToken code redemption | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-04 | Spoofing | decide, switch-account, ConsentRequest, DecideConsent | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-05 | Elevation of privilege | kind checks in both flavours | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-06 | Spoofing | client authentication | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-07 | Spoofing | id_token claims | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-08 | Tampering | signIDToken, jwkFor, Discovery | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-09 | Information disclosure | logs and errors in oidc_*.go | high | mitigate | 19.4-SECURITY-REVIEW.md: fixed in commit eaa545c (test pin; code unchanged) | closed |
| T-19.4-10 | Information disclosure | UserInfo | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-11 | Tampering | identity claim merge | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-12 | Spoofing | refresh grant | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-13 | Information disclosure | subjects | low | mitigate | 19.4-SECURITY-REVIEW.md: fixed in commit eaa545c (test pin; code unchanged) | closed |
| T-19.4-14 | Tampering | MCP regression through shared code (D-09) | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-15 | Information disclosure | models.SigningKey, classes.KeyRing | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-16 | Denial of service | KeyRing lazy first key across instances | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-17 | Tampering | consent and error pages (clickjacking) | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-18 | Tampering | branding CSS and client names in HTML | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-19 | Elevation of privilege | AuthorizationsDestroy | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-20 | Denial of service | token, userinfo, authorize, authorizations | medium | mitigate | 19.4-SECURITY-REVIEW.md: fixed in commit f8eaf70 (test pin; code unchanged) | closed |
| T-19.4-21 | Information disclosure | rate-limit keys | low | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-22 | Spoofing | switch-account and stale sessions | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-23 | Information disclosure | grants of deleted or deactivated users | medium | mitigate | 19.4-SECURITY-REVIEW.md: fixed in commit b4223be (test pin; code unchanged) | closed |
| T-19.4-24 | Elevation of privilege | MCP routes and dynamic registration | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-25 | Spoofing | route shadowing under /oauth/ | low | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-26 | Tampering | sm-user-plugin changes for five consuming applications | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-27 | Information disclosure | client secret display | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-28 | Information disclosure | import of key_dir | high | mitigate | 19.4-SECURITY-REVIEW.md: fixed in commit f8eaf70 (test pin; code unchanged) | closed |
| T-19.4-29 | Tampering | import overwriting live data | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-30 | Denial of service | key rotation and relying-party caches | medium | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-31 | Information disclosure | parity fixtures | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-32 | Elevation of privilege | admin screens | medium | mitigate | 19.4-SECURITY-REVIEW.md: fixed in commit b4223be (test pin; code unchanged) | closed |
| T-19.4-33 | Repudiation | scheduled jobs on several instances | low | accept | accepted, see Accepted Risks Log | closed |
| T-19.4-34 | Denial of service | app.key rotation | low | accept | accepted, see Accepted Risks Log | closed |
| T-19.4-35 | Tampering | scripts/check-phase19.4.sh | medium | mitigate | gate fails closed; `check-phase19.4.sh --self-test` PASS 2026-10-20; git status captured before filtering in the additive check (kept in d9f7bfa) | closed |
| T-19.4-SC | Tampering | Go module installs | high | mitigate | 19.4-SECURITY-REVIEW.md: verified | closed |
| T-19.4-36 | Denial of service / Spoofing | `oauth-token` bucket key | low | accept | found in the security review; see Accepted Risks Log | closed |
| T-19.4-37 | Information disclosure | branding custom CSS and font stylesheet | low | accept | found in the security review; see Accepted Risks Log | closed |
| T-19.4-38 | Spoofing | authorization response | low | accept | found in the security review; see Accepted Risks Log | closed |
| T-19.4-39 | Denial of service | `/oauth/mcp/authorize` | low | accept | found in the security review; see Accepted Risks Log | closed |
*Status: open · closed · open — below high threshold (non-blocking)*
*Severity: critical > high > medium > low — only open threats at or above workflow.security_block_on count toward threats_open*
*Disposition: mitigate (implementation required) · accept (documented risk) · transfer (third-party)*
---
## Accepted Risks Log
| Risk ID | Threat Ref | Rationale | Accepted By | Date |
|---------|------------|-----------|-------------|------|
| AR-19.4-01 | T-19.4-33 | low; leader-elected scheduler, idempotent jobs | plan 03 threat model | 2026-10-19 |
| AR-19.4-02 | T-19.4-34 | low; operator procedure documented | plan 03 threat model | 2026-10-19 |
| AR-19.4-03 | T-19.4-36 | D-18 keeps the PHP keys; secrets, codes and access tokens carry 256 random bits, codes live 60 s and need the PKCE verifier, so online guessing is not feasible; revisit if the bucket keys are ever changed | security review (plan 03 task 7) | 2026-10-19 |
| AR-19.4-04 | T-19.4-37 | the branding administrator is trusted, as in PHP; the consent token alone is useless without the victim's session and is bound to the user; a stricter CSP `style-src` would break the PHP-compatible custom CSS and font URL features | security review (plan 03 task 7) | 2026-10-19 |
| AR-19.4-05 | T-19.4-38 | D-14 PHP parity: relying parties must not notice the switch; clients are administrator-registered first-party apps with exact redirect URIs, so an IdP mix-up needs a second, malicious IdP the relying party trusts | security review (plan 03 task 7) | 2026-10-19 |
| AR-19.4-06 | T-19.4-39 | MCP routes are off by default; the MCP authorize endpoint only parks a request and renders consent, as in phase 08's flavour; add a bucket when an application turns the switch on | security review (plan 03 task 7) | 2026-10-19 |
*Accepted risks do not resurface in future audit runs.*
The code review warnings in `19.4-REVIEW.md` (WR-01..WR-06) are tracked in `19.4-REVIEW-DISPOSITION.md`, not here. WR-01 (a replayed code does not revoke rotated refresh tokens) must be fixed before any client enables refresh.
---
## Security Audit Trail
| Audit Date | Threats Total | Closed | Open | Run By |
|------------|---------------|--------|------|--------|
| 2026-10-19 | 39 | 39 | 0 | security review, plan 03 task 7 (T-19.4-01..34, SC, new 36..39) |
| 2026-10-20 | 40 | 40 | 0 | /msd-secure-phase 19.4 (L1 short-circuit: register authored at plan time, no open threats; T-19.4-35 closed by the gate self-test) |
---
## 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-10-20