Skip to content

Threat model — STRIDE walkthrough

🔒 INTERNAL ONLY. Cites file paths, env vars, and architectural details that are not appropriate for external distribution.

This is a STRIDE-style threat model for the Evospin platform. Per category we list the existing controls (with code citations), gaps (cross-referenced to findings in docs/security/internal/findings.md where one exists), and proposed work. The model is a living document; revisit on every architectural change.

Trust boundaries.

┌────────────┐  HTTPS   ┌─────────┐  Internal   ┌────────┐
│ Player WB  │─────────▶│ ebit-fe │────────────▶│ ebit-  │   ┌──────────┐
│ (browser)  │  WSS     │ (Next)  │   REST      │ api    │──▶│ Postgres │
└────────────┘─────────▶┌─────────┐             │ (Nest) │   └──────────┘
                        │ ebit-rt │  socket.io  │ + bo,  │   ┌──────────┐
                        │ (Nest)  │             │ bj, sr │──▶│  Redis   │
                        └─────────┘             └────────┘   └──────────┘
┌────────────┐  HTTPS   ┌─────────────┐  Internal    │
│ Operator   │─────────▶│ ebit-admin- │  REST        │
│ (browser)  │          │ fe (Next)   │──────────────┘
└────────────┘          └─────────────┘

External-trust boundary: edge load balancer (CDN + WAF — {{TBD: confirm vendor}}). Privileged-trust boundary: admin-fe + bo are operator-only; player-fe + rt are public. Storage-trust boundary: Postgres (primary truth), Redis cache (6379, pwd cache), Redis bot (6380, pwd bot).


S — Spoofing

Existing controls

  • JWT-based session tokens. Issued at auth.service.ts sign-in/MFA-verify; signed with JWT_ACCESS_TOKEN_SECRET (HS256 currently). Expiry per env-config.
  • Refresh-token rotation. Stored server-side in Redis under auth-session:<userId>:<jti>. Logout clears the key.
  • MFA. TOTP-based (speakeasy); enforced by permission.guard.ts for admin operations (subject to SR-004 — SuperAdmin bypass).
  • CAPTCHA on sign-up. RecaptchaService (subject to SR-021 — local bypass via 'pass' token in NODE_ENV=local).
  • Session-per-instance map (apps/rt/src/.../client.gateway.ts) keys websockets by socket.user.id after JWT validation in the WS handshake.
  • Email verification before first sign-in via signed token.
  • Origin-based admin gate (subject to SR-051 — strippable).

Gaps

  • SR-004 (High) — SuperAdmin bypasses MFA; documented. Compensating control: SuperAdmin promotion gated by change-management.
  • SR-011 (High) — JWT-verify and password-reset share signing secret.
  • SR-021 (Medium, Accepted) — CAPTCHA bypass local-env affordance.
  • SR-051 (Low) — Origin-header admin gate.
  • JWT-signing alg. HS256 + shared secret across services. Migrating to RS256 with a JWKS endpoint would let services validate without holding the signing key. Tracked: {{TBD: ADR}}.
  • No device-binding. Stolen access_token cookies replay from any IP / UA. Considered in SR-013 (closed: sessions invalidated on reset) and the sign-out flow, but no per-device binding.

Proposed work

  • Migrate to RS256 + JWKS (Q3-2026, owner platform-auth).
  • Per-device binding via TLS-fingerprint or first-seen UA pinning ({{TBD}}).
  • Strict admin sign-in: SR-051 fix to server-side session check rather than Origin.

T — Tampering

Existing controls

  • Postgres ACID. Bet-place runs in a PrismaTransactional (@bebkovan/prisma-transactional) — balance debit, ledger insert, bet insert all atomic.
  • Provably-fair seed material. Server-seed hash committed before the round, revealed after — popUserSeed in seed service.
  • Bet uniqueness. Bet @@unique([roundId, userId]) prevents double-settle (subject to SR-007 — generic 500 on conflict).
  • Wallet ledger immutability. Transaction rows are append-only; no UPDATE path in the repository.
  • Vault CHECK (vault_amount >= 0) at the schema level (but not on the primary amount column — SR-009).
  • Audit log (AdminActionLog) for admin operations.
  • Class-validator on DTOs (subject to SR-016 — thin coverage).

Gaps

  • SR-002 / SR-009 (Critical / High) — wallet primary balance can go negative (no schema-level CHECK).
  • SR-007 (High) — duplicate-bet protection is constraint-only; no advisory lock.
  • SR-008 (High) — @PlaceBetLock Redis lock can TTL-expire mid-handler.
  • No request signing. Player-FE → API is bearer-token only; no HMAC body signing. Compensating: HTTPS-only + short token TTL.
  • No tamper-proof audit log. AdminActionLog is a regular Postgres table; an admin with DB access could rewrite. {{TBD: append-only / hash-chain audit?}}

Proposed work

  • SR-009 schema CHECK migration (Q2-2026).
  • SR-008 fairness lock — Postgres sequence or conditional nonce++ (Q3-2026).
  • Hash-chain or external-immutable audit log for admin actions (longer-term).

R — Repudiation

Existing controls

  • AdminActionLog — every admin action records actorAdminId, targetUserId, action, params, createdAt.
  • UserSession records on every sign-in (with the isFromRegister=true exception — see legacy WK-2/FM-S-4).
  • Bet ledger is append-only; full transaction history per bet.
  • OpenTelemetry traces every HTTP and BullMQ job, persisted to Tempo via otel-collector (see docs/observability.md).
  • Sentry captures application errors with user context.

Gaps

  • WK-2 / FM-S-4 — first-session sign-up has no UserSession row; admin sees only RegistrationInfo.
  • FM-AUM-2 / SR-047banUser ignores the documented admin parameter; safeLog swallows errors.
  • No central log-retention policy. {{TBD: confirm Loki retention; legacy AF-7 recently fixed Loki gap.}}
  • Audit log not exposed to users for their own actions (no "your activity" feed).

Proposed work

  • SR-047 ban-audit fix (Q3-2026).
  • WK-2 first-session record fix.
  • {{TBD}} log retention SOP.
  • {{TBD}} consider per-user activity feed in player-FE.

I — Information disclosure

Existing controls

  • TLS at edge.
  • JWT-guarded endpoints for player-private data.
  • Role-based guards (PermissionGuard) on admin endpoints.
  • PII segregation: KYC data lives in a separate schema {{TBD: confirm path}}; payment processor (3rd-party) holds card details — we never see PAN.
  • Secrets via Doppler. .env gitignored; templates only in repo.
  • Redis password-protected (cache and bot instances).

Gaps

  • SR-001 (Critical) — anonymous bet-detail endpoint leaks seed material.
  • SR-005 / SR-006 (High) — cross-user bet detail.
  • SR-014, SR-019, SR-020 (Medium) — email-enumeration channels (login timing, reset-cooldown timing, sign-up race error code).
  • SR-031 (Low) — settle-side-effects queue can drop on Redis outage (information loss, not exposure).
  • Logs. Sentry / Loki may capture sensitive payloads — {{TBD: confirm scrubbers in sentry.*.config.ts; current scrub set is generic — password, token}}.
  • OTel attributes. No explicit sanitizer for http.body; default OTel JS exporters don't attach body, but custom instrumentation might. Audit pending.

Proposed work

  • SR-001/005/006 combined fix (Q2-2026).
  • SR-014/019/020 constant-time / consistent-error remediation (Q3-2026).
  • Sentry scrubber audit + extension list (Q3-2026).

D — Denial of service

Existing controls

  • Throttle decorators (@Throttle) on auth endpoints; CAPTCHA on sign-up.
  • WS throttler library (@app/ws-throttler) — rate-limits per socket.
  • DISABLE_RATE_LIMITING env-flag — production must be false. {{TBD: CI assertion.}}
  • BullMQ rate-limiter on outbound jobs.
  • Edge WAF — request size + RPS caps. {{TBD: confirm vendor configuration.}}
  • Redis-backed lockout for repeated bad logins (subject to SR-015 — counter reset).

Gaps

  • SR-010 / SR-018 (High / Medium) — bo route hangs 5 s; small DoS surface.
  • SR-015 (Medium) — lockout-window attempts reset.
  • SR-016 (Medium) — DTO validation does not bound payload size; oversized password reaches bcrypt.
  • SR-017 / SR-030 (Medium / Low) — RT O(n_sockets) fan-out; per-instance socket map blocks horizontal scale.
  • SR-024 (Medium) — speed-roulette queue deadlock on retry-exhaust.
  • @nestjs/microservices (dep advisory) — DoS via recursive handleData on TCP transport. We use Redis transport, not TCP, but the advisory crosses our boundary on patch.

Proposed work

  • SR-016 DTO @MaxLength (Q3-2026).
  • SR-017 socket.io rooms (Q3-2026).
  • SR-030 Redis-backed presence + adapter (Q3-2026).
  • SR-024 watchdog cron (Q3-2026).
  • WAF rule-set audit + DISABLE_RATE_LIMITING CI gate.

E — Elevation of privilege

Existing controls

  • AdminGuard / PermissionGuard on all @admin/* endpoints, subject to SR-004.
  • Role enum with Player, Admin, SuperAdmin; @Roles(...) decorators on protected handlers.
  • MFA gate for admin actions (modulo SR-004).
  • Service isolation. apps/api, apps/rt, apps/bo, apps/bj, apps/speed-roulette run as separate processes with their own ports. Inter-app communication via Redis transport, not direct DB writes from bo.
  • Token scoping. access_token cookies are scoped by domain; admin-* cookies separate from player-*.

Gaps

  • SR-004 (High) — SuperAdmin without mfaSecret bypasses MFA gate.
  • SR-010 (High) — bo HTTP route lacks guards; any JWT holder reaches the message broker.
  • SR-050 / SR-051 (Low) — admin sign-in middleware silent fall-through and Origin-based gate.
  • No fine-grained scopes. Admins are all-powerful within Admin role; no separation between, e.g. "view-only support" and "balance-adjust" personas.
  • DB credentials are shared across all five apps. {{TBD: per-app DB roles?}}

Proposed work

  • SR-004 MFA fix (Q2-2026).
  • SR-010 controller delete (Q2-2026).
  • Sub-roles + scoped tokens (Q4-2026, design pending).
  • Per-app DB roles + RLS where feasible.

Cross-cutting

  • Secrets management. Doppler-injected; rotation cadence: {{TBD: confirm}}. JWT secrets currently rotated only on incident.
  • Backups. Postgres backups: {{TBD: vendor + retention}}. Restore-test cadence: {{TBD}}.
  • Disaster recovery. RTO/RPO documented in docs/business/nfr-sla.md (pending).
  • Threat-model review cadence. Quarterly — owned by security on-call. Next review: {{TBD: 2026-07-15}}.

Cross-references

  • Findings register: docs/security/internal/findings.md
  • Dependency audit: docs/security/internal/dependency-audit.md
  • Architecture weaknesses: docs/weaknesses-register.md; service map + diagrams in docs/architecture/ track
  • Observability: docs/observability.md
  • Customer-facing risk register: docs/security/client/risk-register.md