Skip to content

Threat Modeling

Tiana_ edited this page May 30, 2026 · 1 revision

Threat Modeling

STRIDE-based threat model per service. Identifies trust boundaries, assets, threats, and mitigations. Companion to Architecture-Security, Risk-Register.


Methodology

STRIDE (Microsoft):

  • S - Spoofing identity
  • T - Tampering with data
  • R - Repudiation
  • I - Information disclosure
  • D - Denial of service
  • E - Elevation of privilege

For each service: identify assets → identify trust boundaries → enumerate STRIDE threats → document mitigations.

Reviewed:

  • On every new aggregate / API endpoint
  • After every security-relevant change
  • Annually as part of penetration test prep

Trust boundaries (system-wide)

┌──────────────────────────────┐
│   Public Internet            │   ← UNTRUSTED
└─────────────┬────────────────┘
              │ TLS 1.3, JWT
┌─────────────▼────────────────┐
│   API Gateway                │   ← TRUSTED (semi)
│   (validates JWTs, rate-limits)
└─────────────┬────────────────┘
              │ propagated JWT, cluster-internal
┌─────────────▼────────────────┐
│   Application Services       │   ← TRUSTED
│   (Ledger, Payments, etc.)   │
└─────────────┬────────────────┘
              │ TLS, SASL
┌─────────────▼────────────────┐
│   Data plane                 │   ← TRUSTED
│   (Postgres, Redpanda, Redis)│
└──────────────────────────────┘

External boundaries:
- Bank Adapter ──→ External Bank Provider     (signed HMAC, TLS, allowlisted)
- KYC Adapter  ──→ External KYC Provider      (signed HMAC, TLS)
- LLM Adapter  ──→ External LLM Provider      (TLS, no PII, prompt sanitization)
- Webhook Out  ──→ Subscriber URL             (HMAC-signed, allowlisted)
- Webhook In   ──← External Provider          (HMAC-verified, deduplicated)

Service: API Gateway

Assets

  • JWTs in flight
  • Rate-limit counters
  • TLS private keys
  • JWKS cache

Threats

ID STRIDE Threat Mitigation
GW-S1 Spoofing Forged JWT (wrong signature) RS256 + JWKS validation; reject fast
GW-S2 Spoofing JWT replay after revocation Short TTL (5 min) limits exposure window
GW-S3 Spoofing TLS MITM on public boundary TLS 1.3 mandatory, HSTS, cert pinning at gateway
GW-T1 Tampering JWT claim modification RS256 signature includes claims; tampering breaks signature
GW-T2 Tampering Request body modified between Gateway and service mTLS in cluster (Istio recommended)
GW-R1 Repudiation User denies making a request Audit log includes JWT subject + correlation ID
GW-I1 Info disclosure JWT leaked via logs Logback filter scrubs Authorization header
GW-I2 Info disclosure Error messages leak internal structure RFC 7807 problem details - no stack traces in responses
GW-D1 DoS Volumetric DDoS Cloud-front WAF + per-IP rate limit (Redis token bucket)
GW-D2 DoS Slowloris / connection exhaustion Netty-level connection timeouts, max connections
GW-D3 DoS Expensive endpoint abuse (e.g., heavy report) Per-endpoint rate limits
GW-E1 Privilege escalation Scope claim manipulation Scope check happens after signature verification

Open issues to track

  • WAF integration in deployment guide (Y1 H2)
  • mTLS via Istio operator example (v0.5)

Service: Ledger

Assets

  • Money (account balances)
  • Immutable journal (transactions, entries)
  • Audit log

Threats

ID STRIDE Threat Mitigation
LG-S1 Spoofing Service account compromised, posts unauthorized transactions Vault-issued short-lived creds, audit on credential use
LG-T1 Tampering Direct DB UPDATE bypasses application logic DB role separation: app-role has no UPDATE on entries, transactions (they're append-only); DBA-role audited
LG-T2 Tampering Trigger disabled to bypass invariant Trigger enable/disable monitored; test runs nightly verifies trigger active
LG-T3 Tampering Maintenance script bypasses entries integrity Mandatory PR review for any DB direct-edit script; CI checks
LG-R1 Repudiation Compliance officer denies posting reversal Reverse requires reason (text), resolved_by (user id), audit log
LG-I1 Info disclosure Account names leaked to unauthorized user RBAC LEDGER_READER required; row-level isolation by tenant (v1.5+)
LG-D1 DoS Posting-storm exhausts DB connection pool Per-user rate limit + DB pool sized for 3× burst
LG-D2 DoS Materialized view refresh blocks reads REFRESH MATERIALIZED VIEW CONCURRENTLY
LG-E1 Privilege escalation LEDGER_READER becomes LEDGER_POSTER via JWT mod Mitigated by JWT signature; further scope check at endpoint
LG-E2 Invariant violation Race condition allows SUM != 0 Deferred trigger blocks at COMMIT; SELECT FOR UPDATE on accounts

Critical invariants

  • SUM(entries.amount) = 0 per (transaction_id, currency) - DB-enforced
  • Transactions never deleted, only marked REVERSED - DB role + app discipline
  • Accounts can only transition through allowed states - service-layer state machine

Service: Payment

Assets

  • Payment records
  • Idempotency cache
  • Provider-side payment refs
  • Customer counterparty info (PII)

Threats

ID STRIDE Threat Mitigation
PA-S1 Spoofing Bank webhook signature forged HMAC-SHA256 verification; reject if mismatched
PA-S2 Spoofing Replay attack on bank webhook processed_webhooks dedup; timestamp check
PA-T1 Tampering Provider-controlled fields injected Whitelist of accepted webhook fields; rest ignored
PA-R1 Repudiation User denies initiating payment Audit log + idempotency_key + JWT subject preserved
PA-I1 Info disclosure Counterparty IBAN leaked in logs PII scrubbing in Logback (last-4 only)
PA-I2 Info disclosure Payment amounts visible cross-tenant Tenant isolation (v1.5+); single-tenant per deployment in v0.1
PA-D1 DoS Webhook flood from compromised provider Rate limit per-providerId; alert on burst
PA-D2 DoS Retry storm exhausts bank-adapter executor Bulkhead (Resilience4j), max 20 concurrent bank calls
PA-E1 Privilege escalation Cancel another user's payment Owner-check enforced in service; JWT subject must match payment.created_by

Idempotency-related threats

ID Threat Mitigation
PA-IDEM-1 Cache poisoning via spoofed Idempotency-Key Per-user namespace in cache key; SHA-256 of body validated
PA-IDEM-2 Idempotency replay across users idempotency_keys row stores created_by (creator); cross-user replay rejected

Service: Compliance (KYC, AML, Cases)

Assets

  • KYC evidence references (encrypted)
  • PII (encrypted at rest, never in logs)
  • AML alerts and cases
  • Compliance officer credentials

Threats

ID STRIDE Threat Mitigation
CO-S1 Spoofing KYC provider webhook forged HMAC verification per-provider
CO-S2 Spoofing Unauthorized user impersonates compliance officer MFA mandatory for COMPLIANCE_RESOLVER role
CO-T1 Tampering Case decision modified after resolution Resolved cases immutable (CHECK constraint + service guard)
CO-T2 Tampering Evidence pointer rewritten evidence_external_ref is in encrypted column; tampering breaks decryption
CO-R1 Repudiation Officer denies resolving case Resolution requires reason, resolved_by, resolved_at - audit
CO-I1 Info disclosure KYC evidence leaked At-rest encryption (AES-256-GCM), TLS in transit, log scrubbing
CO-I2 Info disclosure LLM prompts leak PII Prompt sanitization; PII never sent to LLM (only IDs + structured context)
CO-D1 DoS AML rule complexity DoS DSL evaluation timeout (50ms); rule complexity validated at insert
CO-E1 Privilege escalation Resolver becomes admin via JWT mod Same mitigations as Gateway
CO-E2 Privilege escalation Hold case via never-resolving Case TTL alert (open >7 days fires escalation)

Service: Decision Engine

Assets

  • Active rules
  • Decision logs (regulatory retention)

Threats

ID STRIDE Threat Mitigation
DE-S1 Spoofing Unauthorized rule activation DECISION_ADMIN role + audit; activation event published
DE-T1 Tampering Rule definition modified after activation New version always created; old versions immutable
DE-T2 Tampering Decision log entry deleted DB role: app has only INSERT, never DELETE on decision_logs
DE-R1 Repudiation "I never approved that decision" Decision log has full input, matched rules, output, latency, invokedBy
DE-I1 Info disclosure Rule definitions visible to non-DECISION_VIEWER RBAC enforced
DE-D1 DoS Pathological context blows up evaluator DSL operators bounded (no recursive ops); evaluator timeout 100ms

Service: Webhook

Assets

  • Subscriber secrets (encrypted)
  • Delivery history
  • Subscriber URLs (potential SSRF target)

Threats

ID STRIDE Threat Mitigation
WH-S1 Spoofing Subscriber URL forged URL validation: HTTPS only, public IP only (anti-SSRF)
WH-S2 Spoofing HMAC secret leaked enables forged "deliveries" Subscriber controls secret rotation; encrypted at rest
WH-T1 Tampering Delivery history tampered DB role: append-only INSERT on webhook_deliveries
WH-D1 DoS Subscriber requests trigger amplification Per-subscription rate limit; max delivery attempts (7)
WH-I1 Info disclosure Webhook payloads contain PII unintentionally Schema validation on every event type; PII filter at outbox publish
WH-E1 SSRF - internal services scanned via subscription URL URL allowlist, private IP blocked, metadata service blocked (169.254.x.x)

Cross-cutting threats

Supply chain

Threat Mitigation
Dependency with vulnerability Dependabot daily, OWASP DC weekly, Trivy in CI
Dependency with malicious code Maven Central + GitHub-verified publishers; review all version bumps in PR
Compromised container base image Distroless base updated monthly, Trivy scans on each build
CI runner compromised GitHub-hosted only; no self-hosted runners in v0.1
Stolen Maven publishing key OIDC-based publishing (no static keys); cosign signing

Insider threats

Threat Mitigation
Maintainer with full DB access compromised Audited DB queries; separation of app-role vs DBA-role
Compromised contributor PR Required code review; no force-push to main; signed commits
Stolen GitHub credentials MFA enforced; signed commits; branch protection
Backdoor planted in patch release Security review before patch tagging; community signoff for major changes

Cryptographic

Threat Mitigation
RSA private key compromised (JWT signing) Short JWT TTL (5m); JWKS rotation 30 days
AES DEK compromised Per-service DEK from Vault; KEK rotated annually; column re-encryption process documented
HMAC secret compromised Per-provider/per-subscription; rotated quarterly; log forensics on usage

Threat-modeling checklist for new code

When adding a new aggregate / endpoint / external integration:

  • Trust boundary diagram updated
  • STRIDE threats enumerated for the new asset
  • Mitigations documented in this wiki page
  • Endpoint added to Architecture-Security#owasp-top-10 matrix
  • Audit log row written for state-changing actions
  • PII handling checked (encryption at rest, no logs)
  • RBAC role/scope identified and enforced
  • Rate limit applied if user-facing
  • Idempotency required for mutations
  • Tests for spoofed/tampered/replayed inputs

Related

Clone this wiki locally