Threat Model — commercial SaaS surfaces (STRIDE)¶
Read in your language: English · Português · Español
audit-v6 DOCS-ARCH:
SECURITY_ZTP_PREM.mdcovers the on-prem insider-threat model; this document covers the Internet-facing commercial surfaces. It is a living artifact — update it when a surface, data flow, or trust boundary changes.
1. Scope & trust boundaries¶
In scope: app.tlsstress.art (customer-app), admin.tlsstress.art
(admin-console), the Cloudflare edge worker, and the on-prem → SaaS
usage-report ingestion. Out of scope: the isolated NGFW test-bed data plane
(operator-driven lab; separate on-prem threat model in
security/THREAT_MODEL.md).
flowchart LR
U[Customer browser] -->|TLS| E[Cloudflare edge worker]
E -->|auth gate, rate limit, WAF| CA[customer-app]
A[Admin] -->|TLS + MFA| AC[admin-console]
OP[On-prem reporter] -->|signed usage report| CA
CA --> DB[(Postgres — RLS)]
AC --> DB
CA --> ST[Stripe]
ST -->|webhook| CA
CA --> KMS[AWS KMS]
Trust boundaries: browser↔edge, edge↔customer-app, customer-app↔DB (RLS), customer-app↔Stripe, on-prem↔SaaS (signed reports), operator↔admin-console (MFA).
2. STRIDE analysis¶
| # | Threat (STRIDE) | Surface | Mitigation (in place) | Residual / follow-up |
|---|---|---|---|---|
| S1 | Spoofing a customer session | customer-app | HS256 JWT, algorithm fixed server-side (no alg-confusion), timingSafeEqual, exp/iat/iss/aud checks, per-request denylist + credential-epoch staleness (middleware + per-route) |
— |
| S2 | Spoofing the on-prem reporter | usage ingest | Signed usage report + usage_report_nonces (anti-replay) |
Rotate signing keys on a schedule |
| S3 | Spoofing Stripe webhooks | customer-app | HMAC signature verify, fail-closed 503 when secret unset (payment and KYC after audit-v6) | — |
| T1 | Tampering with the audit trail | admin-console/DB | WORM: DB trigger blocks UPDATE + early DELETE; SHA-256 hash chain; signed RFC-5424 export | Register an IANA PEN (currently placeholder) |
| T2 | Tampering with release images | supply chain | Multi-arch build + cosign keyless signing + SBOM; release now on GitHub-hosted runners; all actions SHA-pinned (audit-v6) | — |
| R1 | Repudiation of admin actions | admin-console | admin_audit_events hash chain with actor + source IP + before/after |
SSO + SoD (depends on RBAC epic E1) |
| I1 | Information disclosure cross-tenant | customer-app/DB | Per-query WHERE account_id; RLS tenant_isolation; withTenant() on tenant routes + CI guard test (audit-v6) |
Full default-deny cutover (dual DB roles) |
| I2 | Information disclosure via headers | edge/customer-app | Middleware strips inbound x-admin-*/x-customer-* before setting verified identity; per-request nonce CSP with strict-dynamic |
— |
| I3 | Information disclosure of secrets | all | Fail-closed pepper/KMS; no hardcoded secrets (gitleaks gate); placeholder-secret guard rejects REPLACE_ME (audit-v6) |
— |
| I4 | Information disclosure — KYC/PII | customer-app | Stripe Identity (KYC data stays at Stripe); PII redaction in the audit log | GDPR data-flow map + DPA (privacy track) |
| D1 | Denial of service — auth brute force | customer-app | LRU + Postgres-backed rate limiter, fail-closed | — |
| D2 | Denial of service — edge | edge worker | Rate limit + WAF rules; SLO rate cap | Edge rate-limit fails open on DO error (tracked) |
| D3 | Denial of service — oversized body | customer-app/dashboard | readJson() streams with a byte cap (audit-v6 — chunked-encoding bypass fixed) |
— |
| E1 | Elevation of privilege — IDOR refund | customer-app | Refund route verifies charge.customer == session account before refunding; MFA-gated |
— |
| E2 | Elevation of privilege — KYC self-verify | customer-app | KYC webhook fail-closed on unset secret (audit-v6) — a forged event can no longer self-verify | — |
| E3 | Elevation of privilege — K8s | on-prem control plane | pie-pa-executor pods/exec namespaced; privileged host DaemonSets isolated to dut-system; dashboard hostPath removed (audit-v6) |
web-agents stays PSA privileged — the agents' DSCP/policy-routing init containers need NET_ADMIN/NET_RAW, which baseline rejects at admission (audit-v7 revert) |
3. Assumptions¶
- The Cloudflare edge terminates TLS and is the first WAF/rate-limit layer.
- AWS KMS and Stripe are trusted third parties; their compromise is out of scope.
- Postgres is reachable only from the app tier (PgBouncer); not Internet-exposed.
4. Open items feeding the risk register¶
IANA PEN registration · mTLS/CA-bundle for audit forwarding · SSO + SoD (RBAC E1) · GDPR data-flow map + DPA · full RLS default-deny cutover · edge rate-limit fail-closed. These are tracked in the launch go/no-go and the remediation plan.