Files
nick-doc/09 - Audits/Threat Model - Amanat Escrow Platform.md
Siavash Sameni 4cf5c49274 docs(audit): align documentation with post-remediation backend reality
- Update data model enums to match backend models
- Update API reference auth requirements
- Add dispute module references and warning blocks
- Add 2026-05-24 audit remediation callout to Overview
- Generate task breakdowns and audit artifacts
- Add doc alignment report (.taskmaster/reports/)
2026-05-24 11:16:29 +04:00

573 lines
48 KiB
Markdown

---
title: Threat Model - Amanat Escrow Platform
tags: [audit, security, threat-model, escrow, payments]
created: 2026-05-24
status: living
reviewers: [backend, security, product]
---
# Threat Model - Amanat Escrow Platform
> This document is the canonical threat model for the Amanat (Amn) financial escrow marketplace. All security specifications, authorization matrices, and hardening plans reference this document. It is a living document and must be updated when the attack surface changes.
## Cross-references
- [[Backend Stack Security and Refactor Assessment - 2026-05-24]] -- strategic stack assessment
- [[Platform Logical Audit - 2026-05-24]] -- detailed findings and code-level issues
- [[Security Architecture]] -- current authentication, authorization, and transport security
- [[Backend Architecture]] -- module structure and route registration
- [[Real-time Layer]] -- Socket.IO room model and event catalog
- [[Payment Flow - SHKeeper]] -- SHKeeper pay-in and webhook handling
- [[Payment Flow - DePay & Web3]] -- direct wallet pay-in and on-chain verification
- [[Escrow Flow]] -- escrow state machine and fund custody
- [[Dispute Flow]] -- dispute lifecycle and resolution
- [[Payout Flow]] -- seller payout via SHKeeper or manual admin signing
---
## 1. System Description
### 1.1 What is Amanat
Amanat is a financial escrow marketplace where buyers and sellers transact in cryptocurrency (USDT, USDC on BSC and Ethereum). The platform holds buyer funds in a custodial escrow wallet during order fulfillment and releases or refunds them based on delivery outcome or dispute resolution. It is not a simple CRUD marketplace; it is a financial platform with custody obligations.
### 1.2 Technology stack
| Layer | Technology |
|---|---|
| Frontend | Next.js (React), MUI, wagmi/Web3, Socket.IO client |
| Backend | Express 5, TypeScript, Mongoose, Socket.IO |
| Database | MongoDB (primary), Redis (caching, lockout, rate-limit) |
| Payments - hosted | SHKeeper self-hosted gateway at `pay.amn.gg` |
| Payments - direct | Web3/wagmi wallet transfer to custodial escrow address |
| Blockchain | BSC mainnet (chain ID 56), Ethereum; USDT/USDC BEP-20/ERC-20 |
| Realtime | Socket.IO 4.8 with client-driven room joins |
| Email | Nodemailer via SMTP |
| AI | OpenAI API for content generation, analysis, translation |
| Infrastructure | Docker Compose on single host, Nginx reverse proxy, CloudFlare upstream |
| Deployment | Watchtower auto-pull on `latest` tag (no staging gate) |
### 1.3 Key data flows
**Pay-in to escrow (SHKeeper path):**
Buyer selects offer -> Backend creates Payment intent -> SHKeeper allocates deposit address -> Buyer sends crypto -> SHKeeper webhook confirms -> Backend sets `escrowState=funded` -> Cascade: offer accepted, request status advanced, chat created.
**Pay-in to escrow (Web3 path):**
Buyer connects wallet -> Backend records pending Payment -> Buyer signs on-chain transfer to escrow wallet -> Backend verifies `eth_getTransactionReceipt` -> Sets `escrowState=funded` -> Same cascade.
**Escrow hold:**
Funds sit at custodial BSC wallet. `Payment.escrowState=funded`. No on-chain action occurs. Admin dashboard shows all funded escrows.
**Release to seller:**
Buyer confirms delivery (or auto-release timer) -> Admin initiates payout via SHKeeper Payouts API or manual wallet signing -> On-chain transfer to seller wallet -> `escrowState=released`.
**Refund to buyer:**
Dispute resolved with refund action, or pre-shipment cancellation -> Admin builds refund tx payload -> Admin signs and broadcasts -> `escrowState=refunded`.
**Dispute flow:**
Buyer or seller opens dispute -> Three-way chat created (buyer, seller, admin) -> Admin assigned -> Evidence gathered -> Admin resolves (release/refund/partial/reject) -> Financial side-effect dispatched manually.
**Webhook flow (SHKeeper):**
SHKeeper POSTs to `/api/payment/shkeeper/webhook` with HMAC-SHA256 signature -> Backend verifies raw-body signature -> Deduplicates by `(providerPaymentId, status)` tuple -> PaymentCoordinator serializes state update -> Cascade to offer/request/chat/notification.
---
## 2. Protected Assets
| Asset Class | Examples | Sensitivity | Storage |
|---|---|---|---|
| **User credentials** | bcrypt-hashed passwords, email | Critical | MongoDB `users` |
| **User sessions** | JWT access (7d), refresh (30d) | Critical | `localStorage` (browser), MongoDB `user.refreshTokens[]` |
| **Admin credentials and sessions** | Same as user but with `role: admin` | Critical | Same as user |
| **Payment records and funds state** | `Payment.status`, `escrowState`, amounts, tx hashes | Critical | MongoDB `payments` |
| **Escrow wallet private key** | Key controlling `ESCROW_WALLET_ADDRESS` | Critical | Platform operator custody (not in code) |
| **Wallet addresses** | Buyer wallet, seller payout wallet, escrow wallet | High | MongoDB `users.profile.walletAddress`, `Payment.blockchain` |
| **Webhook secrets** | `SHKEEPER_WEBHOOK_SECRET` | Critical | Env var |
| **API keys** | `SHKEEPER_API_KEY`, `OPENAI_API_KEY`, `JWT_SECRET` | Critical | Env var |
| **User personal data** | Email, name, phone, addresses | High | MongoDB `users` |
| **Transaction history** | All Payment, PurchaseRequest, Dispute records | High | MongoDB |
| **Private messages/chat** | Chat messages, dispute evidence | High | MongoDB `chats` |
| **Realtime event streams** | Socket.IO room broadcasts, notifications | Medium | In-process / Socket.IO |
| **Provider integration configs** | SHKeeper endpoint, callback URLs, CORS origin | High | Env var, hardcoded |
| **AI usage and budgets** | OpenAI call volume per user | Medium | No tracked storage currently |
| **Upload files** | Avatars, product images, dispute evidence | Medium | Host filesystem `uploads/` |
| **Notification preferences** | Per-user notification settings | Low | MongoDB `users` |
| **Blog content** | Posts, categories | Low | MongoDB `blogposts` |
---
## 3. Actors
| Actor | Access Level | Motivation | Risk Profile |
|---|---|---|---|
| **Buyer** | Authenticated (`role: buyer`). Can create requests, pay, confirm delivery, open disputes, rate. | Complete purchase, get refund if unsatisfied, dispute bad service. May attempt to bypass payment or confirm without delivery. | Medium. Motivated to fraud payment or delivery confirmation. |
| **Seller** | Authenticated (`role: seller`). Can create offers, templates, shop settings, receive payouts. | Receive payment for goods/services. May attempt to mark unshipped items as delivered or inflate prices post-acceptance. | Medium. Motivated to receive funds without delivery. |
| **Admin** | Authenticated (`role: admin`). Full access to user management, dispute resolution, manual payouts, payment dashboard. | Platform operations. Single point of trust for fund release. | High. If compromised, can release all escrowed funds. |
| **Support staff** | Authenticated (`role: support`). Read-only access, password resets, escalation. | Customer support operations. | Low-Medium. Limited destructive capability but can read sensitive data. |
| **Unauthenticated attacker** | No auth. Can reach public endpoints, Socket.IO handshake (rejected without JWT), webhook route (requires HMAC). | Financial gain, data theft, service disruption, cost abuse. | High. Broad attack surface including unauthenticated endpoints. |
| **Compromised user** | Valid JWT for a buyer/seller account. | Attacker uses stolen session to redirect payouts, open fraudulent disputes, exfiltrate data. | High. Hard to distinguish from legitimate activity. |
| **Compromised admin** | Valid JWT with admin role. | Full platform compromise: release escrows, manipulate payments, exfiltrate all user data. | Critical. Single compromised admin session can drain the escrow wallet. |
| **Malicious payment provider / webhook sender** | Can POST to webhook endpoint. | Inject false payment confirmations to trigger release. | High. If webhook verification is flawed, funds are at risk. |
| **Malicious blockchain actor** | Can submit any tx hash for verification. | Submit a tx hash for an unrelated successful transfer (wrong recipient/amount) and get it accepted as payment. | High. Current Web3 verification is incomplete. |
| **Insider threat** | Legitimate access to infrastructure, code, or admin tools. | Fund manipulation, data exfiltration, backdoor insertion. | Critical. Trust boundary cannot be fully technical. |
| **Supply-chain attacker** | Can compromise npm packages used by the project. | Remote code execution, credential theft, payment flow manipulation. | High. Real 2026 precedent with Axios, TanStack compromises. |
---
## 4. Trust Boundaries
### 4.1 Diagram
```mermaid
flowchart TB
subgraph Internet["INTERNET (Untrusted)"]
Browser["Browser / Next.js"]
MobileApp["Telegram Mini App"]
Attacker["Attacker"]
end
subgraph Edge["EDGE (Semi-Trusted)"]
CF["CloudFlare / Nginx"]
end
subgraph Backend["BACKEND (Trusted - single Docker host)"]
API["Express API\n:5001"]
SocketIO["Socket.IO Server"]
Uploads["/uploads filesystem"]
end
subgraph Data["DATA LAYER (Trusted)"]
Mongo[("MongoDB\n:27017")]
Redis[("Redis\n:6379")]
end
subgraph External["EXTERNAL SERVICES (Semi-Trusted)"]
SHK["SHKeeper\npay.amn.gg"]
BSC["BSC/Eth RPC\nbsc-dataseed"]
SMTP["SMTP / Email"]
OpenAI["OpenAI API"]
end
Browser -- "HTTPS / REST\nJWT in Authorization" --> CF
Browser -- "WSS / Socket.IO\nJWT in handshake" --> CF
MobileApp -- "HTTPS" --> CF
Attacker -.-> CF
CF -- "HTTP proxy" --> API
CF -- "WS proxy" --> SocketIO
API -- "Mongoose queries" --> Mongo
API -- "Cache / lockout / rate-limit" --> Redis
API -- "File read" --> Uploads
SocketIO -- "Room membership" --> Redis
API -- "POST /api/v1/payment_request\nX-Shkeeper-Api-Key" --> SHK
SHK -- "POST /api/payment/shkeeper/webhook\nHMAC-SHA256" --> API
API -- "eth_getTransactionReceipt" --> BSC
API -- "Nodemailer send" --> SMTP
API -- "GPT calls" --> OpenAI
```
### 4.2 Boundary descriptions
| Boundary | Crossing Protocol | Trust Assumption | Current Gap |
|---|---|---|---|
| Browser <-> Backend API | HTTPS, JWT in `Authorization` header, CORS allow-list | Browser is untrusted; JWT proves identity | Tokens stored in `localStorage` (XSS-exposed); 7-day access token lifetime |
| Browser <-> Socket.IO | WSS, JWT in handshake `auth.token` | Socket is untrusted until JWT verified | Room joins are client-driven with `userId` argument; server-side verification may be incomplete (`socketService.ts` citation needed) |
| Backend <-> MongoDB | Mongoose on `localhost:27017` (no TLS in dev) | Database is trusted; network is trusted (single host) | No TLS, no auth on MongoDB connection in default config |
| Backend <-> Redis | Redis client on `localhost:6379` | Redis is trusted; used for cache and lockout | No TLS; password optional (`REDIS_PASSWORD`) |
| Backend <-> SHKeeper | HTTPS, `X-Shkeeper-Api-Key` header outbound; HMAC-SHA256 inbound | SHKeeper is semi-trusted (self-hosted, operator controls) | Webhook returns 202 on all errors; no dead-letter queue; signature bypass in dev mode |
| Backend <-> Blockchain RPC | HTTPS JSON-RPC to `bsc-dataseed.binance.org` | RPC is semi-trusted (public endpoint, rate-limited) | Only `receipt.status` checked, not `Transfer` event recipient/amount; public RPC subject to throttling |
| Backend <-> Email (SMTP) | SMTP with credentials | SMTP provider is trusted | Verification codes logged in plaintext to stdout in all environments |
| Backend <-> OpenAI | HTTPS, `OPENAI_API_KEY` | OpenAI is trusted for data handling | No authentication required on AI endpoints; no per-user budget tracking |
| Admin UI <-> Backend | Same as Browser <-> Backend but with `role: admin` JWT | Admin is highly trusted | No step-up auth for payouts; admin role not verified on all admin routes |
| Mini App / Telegram <-> Backend | HTTPS, Telegram WebApp initData | Telegram identity is semi-trusted | Deep-link parameter tampering surface; `initData` validation must be strict |
---
## 5. Threat Catalog
### T01 -- Fake Payment Proof Submission
| Field | Value |
|---|---|
| **STRIDE** | Spoofing |
| **Affected assets** | Payment records, escrow funds |
| **Actors** | Buyer, malicious blockchain actor |
| **Description** | In the Web3 payment path, the backend accepts a `transactionHash` and verifies it via `eth_getTransactionReceipt`. The current `BSCTransactionVerifier` checks only `receipt.status === '0x1'` (transaction succeeded). It does not decode the ERC-20 `Transfer` event to verify that `to` matches `ESCROW_WALLET_ADDRESS` or that `value` matches the expected payment amount. A buyer can submit the hash of any successful USDT transfer (e.g., a 0.01 USDT transfer to their own wallet) and the system marks the payment as `completed` with `escrowState=funded`. |
| **Current state** | Vulnerable |
| **Required mitigation** | Decode `Transfer(address,address,uint256)` event from `receipt.logs`. Assert `to == ESCROW_WALLET_ADDRESS` and `value >= expectedAmount` (with decimal adjustment). Reject if mismatch. Reference: [[Payment Flow - DePay & Web3]], `decentralizedPaymentService.ts`. |
### T02 -- Webhook Replay Attack
| Field | Value |
|---|---|
| **STRIDE** | Spoofing / Tampering |
| **Affected assets** | Payment records, escrow funds |
| **Actors** | Malicious webhook sender, man-in-the-middle |
| **Description** | SHKeeper webhooks are verified using HMAC-SHA256 of the raw body against `SHKEEPER_WEBHOOK_SECRET`. However: (a) in development mode, signature verification is bypassed entirely; (b) the handler deduplicates by comparing `(metadata.shkeeperStatus, balance_fiat, paid)` within a 10-second window, but a replay outside that window with identical payload could be processed again; (c) there is no nonce, timestamp, or delivery-ID check from SHKeeper. An attacker who captures a valid webhook payload can replay it to mark a different payment as paid. |
| **Current state** | Partially mitigated |
| **Required mitigation** | Implement webhook delivery-ID tracking (store `delivery_id` or `(external_id, status)` as idempotency key). Reject webhooks older than a configurable window. Never bypass signature verification in any non-local environment. Add dead-letter storage for failed webhooks. Reference: [[Payment Flow - SHKeeper]], `shkeeperWebhook.ts`. |
### T03 -- Arbitrary Socket.IO Room Join
| Field | Value |
|---|---|
| **STRIDE** | Information disclosure / Elevation of privilege |
| **Affected assets** | Realtime event streams, private notifications, chat messages |
| **Actors** | Compromised user, unauthenticated attacker (with stolen JWT) |
| **Description** | Socket.IO room joins are client-driven. The client emits events like `join-user-room {userId}`, `join-seller-room {sellerId}`, `join-chat-room {chatId}`. The documentation explicitly warns that `userId` arguments "are NOT trusted blindly" but notes the authorization check in `socketService.ts` "needs verification." If the server does not verify `socket.data.user.id === userId` before joining, any authenticated user can subscribe to any other user's private notifications (`user-{otherUserId}`), seller events (`seller-{otherSellerId}`), or chat rooms (`chat-{otherChatId}`). |
| **Current state** | Vulnerable (verification status uncertain) |
| **Required mitigation** | Remove client-driven `join-*-room` events. After JWT handshake, the server should automatically join the socket to `user-{decoded.id}`. For role rooms (`seller-*`, `buyer-*`), derive membership from `decoded.role`. For chat rooms, verify that `decoded.id` is in `chat.participants`. Reference: [[Real-time Layer]], `socketService.ts`. |
### T04 -- Stolen Token Reuse
| Field | Value |
|---|---|
| **STRIDE** | Spoofing / Elevation of privilege |
| **Affected assets** | User sessions, admin sessions, all user-owned data |
| **Actors** | Compromised user, compromised admin |
| **Description** | Access tokens are JWTs stored in `localStorage` with a 7-day expiry. If an XSS vulnerability exists (or a compromised npm dependency injects a script), the attacker exfiltrates both `accessToken` and `refreshToken` from `localStorage`. The refresh token rotation mechanism detects reuse (if the same refresh token is presented twice, all sessions are invalidated), but the attacker can use the access token for up to 7 days without triggering rotation. For admin accounts, a stolen token gives full platform control including manual payout signing. |
| **Current state** | Partially mitigated (rotation exists, but storage and lifetime are weak) |
| **Required mitigation** | (1) Move refresh tokens to `httpOnly` secure cookies. (2) Reduce access token lifetime to 15-60 minutes. (3) Keep only the access token in memory (not localStorage). (4) Implement CSRF protection if cookies are used. Reference: [[Security Architecture]] section 11, [[Authentication Flow]]. |
### T05 -- Double Payout / Double Release
| Field | Value |
|---|---|
| **STRIDE** | Tampering / Repudiation |
| **Affected assets** | Payment records, escrow funds |
| **Actors** | Compromised admin, race condition (concurrent requests) |
| **Description** | The payout flow (`shkeeperPayoutService.createPayoutTask`) has idempotency: it checks for an existing outgoing Payment with the same `(purchaseRequestId, sellerOfferId, sellerId, provider, direction:'out')` in `pending/processing/completed` status. However: (a) the manual admin payout path (`admin-wallet-payout.tsx`) signs on-chain directly and then calls `confirmAdminTx`, with no corresponding idempotency guard; (b) the `PaymentCoordinator` serializes updates for a single payment, but two concurrent payout requests for different payments against the same escrow balance are not guarded; (c) `escrowState` is a mutable field, not derived from an immutable ledger. An admin (or attacker with admin credentials) could trigger two payouts for the same underlying funds. |
| **Current state** | Partially mitigated |
| **Required mitigation** | (1) Introduce an immutable funds ledger (`FundsAccount`, `LedgerEntry`) so that releasable balance is derived, not stored. (2) Add a distributed lock (Redis) around payout creation for a given escrow. (3) Require admin step-up authentication or two-person approval for payouts above a threshold. Reference: [[Escrow Flow]], [[Payout Flow]], [[Backend Stack Security and Refactor Assessment - 2026-05-24]] Phase 2. |
### T06 -- Dispute Bypass (Release During Active Dispute)
| Field | Value |
|---|---|
| **STRIDE** | Tampering |
| **Affected assets** | Escrow funds, dispute records |
| **Actors** | Compromised admin, buyer (if auto-release fires) |
| **Description** | Opening a dispute does not change `Payment.escrowState` away from `funded`. The Dispute Flow explicitly documents this: "Today, opening a dispute does not flip `Payment.escrowState` away from `funded`. An admin could theoretically still release the escrow before resolving the dispute." The Dispute API claims dispute creation "pauses any in-flight payout," but the implementation does not enforce this in the `PaymentCoordinator` or `PayoutService`. A release or refund can proceed while a dispute is active. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Add a `disputed` escrow state or a `disputeHold` flag on Payment that blocks all release/refund operations. (2) Enforce this in `PaymentCoordinator` and `shkeeperPayoutService` as a precondition. (3) Auto-set this flag in `DisputeService.createDispute`. (4) Clear only when dispute is resolved. Reference: [[Dispute Flow]], [[Escrow Flow]], [[Platform Logical Audit - 2026-05-24]] Finding 1. |
### T07 -- Email / Registration Abuse
| Field | Value |
|---|---|
| **STRIDE** | Denial of service / Spoofing |
| **Affected assets** | Email service, user accounts |
| **Actors** | Unauthenticated attacker |
| **Description** | The registration endpoint (`POST /api/auth/register`) triggers an email dispatch for every request. The resend endpoint (`POST /api/auth/resend-verification`) does the same. Rate limiting is disabled globally. An attacker can submit thousands of registration requests with random or victim email addresses, causing: (a) SMTP cost explosion; (b) email service reputation damage (spam complaints); (c) `TempVerification` collection bloat in MongoDB. Additionally, the 6-digit verification code is logged to stdout in all environments, leaking it to anyone with log access. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Enable rate limiting on `/api/auth/register` and `/api/auth/resend-verification` (e.g., 5 requests per 15 minutes per IP, 3 per email). (2) Remove `console.log` of verification codes in production. (3) Add CAPTCHA or proof-of-work for registration. (4) Add TTL index on `TempVerification` collection for auto-cleanup. Reference: [[Registration Flow]], [[Security Architecture]] section 9. |
### T08 -- AI Cost Abuse (Unauthenticated / Over-Budget Usage)
| Field | Value |
|---|---|
| **STRIDE** | Denial of service (financial) |
| **Affected assets** | AI usage budgets, `OPENAI_API_KEY` cost |
| **Actors** | Unauthenticated attacker, compromised user |
| **Description** | All AI endpoints (`/api/ai/generate`, `/api/ai/analyze`, `/api/ai/translate`, `/api/ai/assist`) currently have no authentication or caller identity verification, as documented in the Platform Logical Audit. Any client can call these endpoints and incur OpenAI API costs. Even if authentication is added, there is no per-user budget tracking, daily limit, or cost attribution. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Require Bearer JWT on all AI routes. (2) Implement per-user daily/monthly token budgets stored in MongoDB or Redis. (3) Rate-limit AI endpoints separately (e.g., 20 requests per hour per user). (4) Alert on abnormal cost spikes. Reference: [[Platform Logical Audit - 2026-05-24]] Finding 3. |
### T09 -- Admin Privilege Escalation
| Field | Value |
|---|---|
| **STRIDE** | Elevation of privilege |
| **Affected assets** | All platform assets |
| **Actors** | Compromised user, insider |
| **Description** | Admin role is stored as a string field `role: 'admin'` on the User document. If any route or service fails to enforce `roleGuard('admin')`, an ordinary user can access admin-only endpoints. The audit identifies that "admin-sensitive routes need explicit role enforcement, not just authentication." The admin bootstrap seed creates an admin with known credentials; if these are not rotated before launch, anyone who knows the seed email/password gains admin access. Additionally, there is no step-up authentication for high-risk admin operations (payouts, role changes, user deletion). |
| **Current state** | Partially mitigated (roleGuard exists but may not be applied consistently) |
| **Required mitigation** | (1) Audit every admin route for `roleGuard('admin')` enforcement. (2) Rotate all seed admin credentials before launch. (3) Implement step-up authentication (re-authentication or 2FA) for payouts, role changes, and user suspension. (4) Add audit logging for all admin actions. Reference: [[Security Architecture]] section 3, [[Backend Architecture]] section 4. |
### T10 -- Passkey / WebAuthn Bypass
| Field | Value |
|---|---|
| **STRIDE** | Spoofing |
| **Affected assets** | User credentials, sessions |
| **Actors** | Unauthenticated attacker, compromised user |
| **Description** | The passkey implementation has three critical flaws: (a) The `publicKey` field is stored as the literal string `'simulated-public-key'` rather than the actual COSE public key from the attestation object. A malicious client can register any credential ID under any user account. (b) Challenges are stored in an in-process `Map`, not Redis, breaking in multi-instance deployments. (c) Passkey-issued refresh tokens are not appended to `user.refreshTokens[]`, so the standard refresh endpoint rejects them, and the token reuse detection mechanism does not apply. |
| **Current state** | Vulnerable (passkeys are stubbed and non-functional for security) |
| **Required mitigation** | (1) Replace stub attestation with `@simplewebauthn/server`. Store real COSE public keys. (2) Move challenge storage to Redis with TTL. (3) Persist passkey-issued refresh tokens in `user.refreshTokens[]`. (4) Enforce monotonic counter to detect cloned authenticators. (5) Consider disabling passkey feature entirely until production-ready. Reference: [[Passkey (WebAuthn) Flow]], [[Platform Logical Audit - 2026-05-24]] Finding 2. |
### T11 -- Web3 Payment with Wrong Recipient / Token / Amount
| Field | Value |
|---|---|
| **STRIDE** | Spoofing |
| **Affected assets** | Payment records, escrow funds |
| **Actors** | Buyer, malicious blockchain actor |
| **Description** | Closely related to T01 but distinct in scope. The `POST /api/payment/decentralized/save` and `POST /api/payment/decentralized/update` endpoints are unauthenticated (per the audit findings). This means any client can create or modify decentralized payment records in the database without any on-chain verification. Combined with the incomplete `BSCTransactionVerifier`, an attacker can: (a) save a fake payment with arbitrary status; (b) update confirmations to make it appear verified; (c) trigger the cascade that accepts an offer and advances the purchase request. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Add authentication to all `/api/payment/decentralized/*` endpoints. (2) Add ownership verification (the authenticated user must be the buyer of the referenced purchase request). (3) Harden `BSCTransactionVerifier` per T01. (4) Remove test endpoints from production builds. Reference: [[Platform Logical Audit - 2026-05-24]] Finding 3, [[Payment Flow - DePay & Web3]]. |
### T12 -- Rate Limit Bypass
| Field | Value |
|---|---|
| **STRIDE** | Denial of service |
| **Affected assets** | All endpoints, email service, AI budget, database |
| **Actors** | Unauthenticated attacker |
| **Description** | `express-rate-limit` is explicitly disabled in the Express application (`app.ts:227`). The documentation describes recommended limits but they are not active. Without rate limiting, every endpoint is exposed to abuse: brute-force login attempts, registration email spam, AI cost explosion, payment endpoint flooding, and chat/message spam. Redis-based counters are wired but not enforced. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Enable rate limiting globally with tiered limits: auth paths (5-10 req/5 min/IP), payment paths (20 req/15 min/user), AI paths (20 req/hr/user), chat/messaging (30 req/min/user), file upload (10 req/hr/user), global default (100 req/15 min/IP). (2) Store counters in Redis. (3) Return `429 Too Many Requests` with `Retry-After` header. Reference: [[Security Architecture]] section 9, [[Backend Architecture]] section 3. |
### T13 -- XSS Leading to Token Theft
| Field | Value |
|---|---|
| **STRIDE** | Tampering / Information disclosure |
| **Affected assets** | User sessions, admin sessions |
| **Actors** | Unauthenticated attacker (via stored XSS), compromised user (via reflected XSS) |
| **Description** | Access tokens and refresh tokens are stored in `localStorage`. Any XSS vulnerability -- whether from a stored payload in chat messages, product descriptions, blog posts, dispute evidence, or user profile fields -- allows an attacker to read both tokens via `localStorage.getItem()`. Helmet CSP is in place but configured permissively for Web3 popup compatibility (`COEP: unsafe-none`, `COOP: same-origin-allow-popups`). React auto-escapes output, but `dangerouslySetInnerHTML` may be used in blog or description rendering. File uploads (dispute evidence, chat attachments) are served from `/uploads` without authentication, and a malicious SVG or HTML file could execute scripts in the context of the application. |
| **Current state** | Partially mitigated (CSP, React escaping, MIME validation exist) |
| **Required mitigation** | (1) Move tokens out of `localStorage` per T04. (2) Tighten CSP; evaluate whether `unsafe-none` COEP is necessary. (3) Audit all uses of `dangerouslySetInnerHTML`. (4) Serve uploads with `Content-Disposition: attachment` and `X-Content-Type-Options: nosniff`. (5) Add virus scanning (ClamAV) for uploaded files. Reference: [[Security Architecture]] section 11, section 7. |
### T14 -- npm Supply-Chain Compromise
| Field | Value |
|---|---|
| **STRIDE** | Tampering / Elevation of privilege / Information disclosure |
| **Affected assets** | All backend and frontend code, secrets, user data |
| **Actors** | Supply-chain attacker |
| **Description** | Both frontend and backend depend heavily on npm packages. The 2026 ecosystem has documented real compromises: Axios npm supply-chain compromise (March 2026, used in this project), TanStack npm compromise (May 2026, `@tanstack/react-query` used in this project), Express security releases including Multer issues (February 2026, `multer: ^2.0.2` specified). A compromised dependency can exfiltrate `JWT_SECRET`, `SHKEEPER_WEBHOOK_SECRET`, `OPENAI_API_KEY`, or inject malicious code into payment flows. The backend currently has no lockfile pinning verification, no npm provenance checking, and no dependency audit CI step. |
| **Current state** | Partially mitigated (some packages may be updated) |
| **Required mitigation** | (1) Pin all dependencies with lockfile; verify lockfile integrity in CI. (2) Enable npm provenance/signature verification where available. (3) Run `npm audit` / `yarn audit` in CI; fail on high/critical CVEs. (4) Review and update `multer` to >= 2.1.0. (5) Implement a secure build and supply-chain policy (see [[Backend Stack Security and Refactor Assessment - 2026-05-24]] doc 10). (6) Consider extracting security-critical backend code to Go/Kotlin with smaller dependency footprint. Reference: [[Backend Stack Security and Refactor Assessment - 2026-05-24]] supply-chain section. |
### T15 -- CSRF on State-Changing Endpoints
| Field | Value |
|---|---|
| **STRIDE** | Cross-site request forgery |
| **Affected assets** | User account settings, payment actions, admin operations |
| **Actors** | Unauthenticated attacker (via victim browser) |
| **Description** | The current architecture uses JWT in the `Authorization` header (not cookies), which provides inherent CSRF protection because browsers do not automatically attach `Authorization` headers on cross-origin requests. However, if the planned migration to `httpOnly` cookies for refresh tokens (recommended in T04) is implemented without CSRF tokens, the refresh endpoint becomes vulnerable to CSRF. Additionally, if any endpoint accidentally reads tokens from cookies or query parameters, CSRF becomes possible. |
| **Current state** | Mitigated (JWT in Authorization header) |
| **Required mitigation** | When migrating to `httpOnly` cookies: (1) Implement CSRF token (double-submit cookie or synchronizer token). (2) Apply `SameSite=Strict` or `SameSite=Lax` on cookies. (3) Verify `Origin` header on state-changing requests. (4) Ensure CORS is not overly permissive. Reference: [[Security Architecture]] section 4. |
### T16 -- Deep-Link / Parameter Tampering (Telegram Surface)
| Field | Value |
|---|---|
| **STRIDE** | Tampering / Spoofing |
| **Affected assets** | User identity, referral attribution |
| **Actors** | Unauthenticated attacker |
| **Description** | The Telegram Mini App surface accepts `initData` from the Telegram WebApp SDK. If the backend does not cryptographically verify `initData` using the Telegram Bot token, an attacker can forge the user identity, impersonate other Telegram users, or tamper with referral parameters in deep links (`/r/:code` redirect at `app.ts:274-278`). The referral code is passed as a query parameter and is not validated against a signed token. |
| **Current state** | Partially mitigated (verification status depends on implementation) |
| **Required mitigation** | (1) Verify Telegram `initData` signature using HMAC-SHA256 with the Bot token on every Mini App request. (2) Validate referral codes server-side before applying. (3) Do not trust client-supplied userId from Telegram context without signature verification. |
### T17 -- Payment Provider Outage / Chain RPC Outage
| Field | Value |
|---|---|
| **STRIDE** | Denial of service |
| **Affected assets** | Payment processing, escrow operations, user experience |
| **Actors** | Infrastructure failure (non-malicious) |
| **Description** | The platform depends on two external systems for financial operations: (a) SHKeeper gateway at `pay.amn.gg` for pay-in address allocation and payout execution; (b) BSC/Ethereum RPC (`bsc-dataseed.binance.org`) for transaction verification. If SHKeeper is down, new pay-in intents cannot be created (the code falls back to a demo URL in this case). If the RPC is down, Web3 payment verification cannot complete. The circuit breaker (`shkeeperFetch` in `shkeeperHealthCheck.ts`) trips after repeated failures, but the fallback is a demo URL, not a graceful degradation. Public BSC RPC endpoints are rate-limited. There is no dedicated RPC provider. |
| **Current state** | Partially mitigated (circuit breaker and wallet monitor exist as fallbacks) |
| **Required mitigation** | (1) Use a dedicated RPC provider (Ankr, QuickNode, Alchemy) with SLA guarantees. (2) Implement retry with exponential backoff for verification. (3) When SHKeeper is down, return a clear error to the buyer instead of a demo URL. (4) Store pending verifications for reconciliation when service resumes. (5) Document operational runbooks for provider outage. Reference: [[Payment Flow - SHKeeper]], [[Payment Flow - DePay & Web3]]. |
### T18 -- Insider Fund Manipulation
| Field | Value |
|---|---|
| **STRIDE** | Tampering / Repudiation |
| **Affected assets** | Escrow funds, payment records |
| **Actors** | Insider threat (admin, operator, developer) |
| **Description** | The escrow wallet is a single custodial BSC address controlled by the platform operator. The private key has unrestricted access to all in-flight escrowed funds. Admin users can initiate manual payouts via the `admin-wallet-payout.tsx` UI by connecting their wallet and signing transfers directly. There is no multi-signature requirement, no dual-approval for large payouts, no immutable audit ledger, and no automated anomaly detection on payout patterns. A single insider with access to the escrow private key or an admin session can drain all escrowed funds. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Move escrow wallet to a multi-sig solution (e.g., Gnosis Safe) requiring at least 2-of-3 signatures for any transfer. (2) Implement two-person approval for payouts above a configurable threshold. (3) Build an immutable funds ledger so every debit/credit is append-only and auditable. (4) Implement real-time alerting on unusual payout patterns. (5) Store one key in HSM. (6) Separate admin UI access from wallet signing capability. Reference: [[Escrow Flow]] custodial risk warning, [[Payout Flow]], [[Backend Stack Security and Refactor Assessment - 2026-05-24]] Phase 2. |
### T19 -- Seller Price Manipulation After Offer Acceptance
| Field | Value |
|---|---|
| **STRIDE** | Tampering |
| **Affected assets** | Payment amounts, escrow funds |
| **Actors** | Seller |
| **Description** | The Negotiation Flow documents that "`updateOffer` does not enforce status... Current code allows the price change, which is dangerous post-payment." A seller can modify the offer price after the buyer has already paid, potentially inflating the amount or changing terms. The escrow holds the original amount, but the displayed/agreed amount could be tampered with. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Reject `updateOffer` if `status !== 'pending'`. (2) Snapshot offer amount at payment creation time in the Payment document. (3) Use the Payment-embedded amount for all downstream operations, not the live offer. Reference: [[Platform Logical Audit - 2026-05-24]] Finding 18. |
### T20 -- Delivery Confirmation Brute Force
| Field | Value |
|---|---|
| **STRIDE** | Spoofing |
| **Affected assets** | Escrow release trigger |
| **Actors** | Buyer |
| **Description** | The delivery confirmation code is a 6-digit number (`Math.floor(100000 + Math.random()*900000)`) with 900,000 possible combinations. There is no rate limiting on verification attempts. The "manual fast-track" allows the buyer to confirm delivery at any time, even before the seller has shipped. A buyer could brute-force the confirmation code to trigger release without actually receiving the item. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Add Redis-backed rate limiting on delivery code verification (e.g., 5 attempts per 15 minutes per request). (2) Increase code entropy (alphanumeric, longer). (3) Restrict fast-track confirmation to `status: 'delivery'` only. (4) Add time-based invalidation. Reference: [[Platform Logical Audit - 2026-05-24]] Finding 8. |
### T21 -- Unauthenticated Data Exfiltration
| Field | Value |
|---|---|
| **STRIDE** | Information disclosure |
| **Affected assets** | User personal data, payment history, notifications |
| **Actors** | Unauthenticated attacker |
| **Description** | Multiple endpoints accept a `userId` parameter without authentication or ownership verification: `GET /api/payment/decentralized/history/:userId` returns any user's payment history; the legacy notification router accepts `?userId=` query parameter allowing notification read/modify for any user. The payment history endpoint exposes transaction hashes, amounts, counterparties, and wallet addresses. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Add Bearer JWT authentication to all data-access endpoints. (2) Enforce ownership: the authenticated user's ID must match the requested `userId` (or require admin role). (3) Audit all parameterized routes for missing auth middleware. Reference: [[Platform Logical Audit - 2026-05-24]] Finding 3. |
### T22 -- Verification Code Leakage via Logs
| Field | Value |
|---|---|
| **STRIDE** | Information disclosure |
| **Affected assets** | User credentials |
| **Actors** | Insider threat, log viewer |
| **Description** | The registration and password reset controllers log the generated 6-digit verification code to stdout via `console.log` in all environments, including production. Anyone with access to application logs (CloudWatch, Sentry breadcrumbs, docker logs) can read these codes and take over unverified accounts or reset passwords. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Guard all verification code logging behind `if (NODE_ENV !== 'production')`. (2) Implement a log redaction formatter that strips sensitive fields. (3) Audit all `console.log` and `logger` calls for sensitive data. Reference: [[Registration Flow]] warning. |
### T23 -- Payment State Machine Inconsistency
| Field | Value |
|---|---|
| **STRIDE** | Tampering / Repudiation |
| **Affected assets** | Payment records, escrow state |
| **Actors** | Race conditions, software bugs |
| **Description** | The platform has multiple overlapping state machines for the same entities. Payment has both `status` and `escrowState` that must remain consistent. The Dispute entity has three mutually incompatible status/action enum sets across the Data Model, API Reference, and Flow documents. The PurchaseRequest has different status enums across three documents. Ghost states exist (`confirmed`, `partial`, `finalized`, `archived`) that no automated flow sets. This inconsistency creates opportunities for state confusion where a payment appears in one state from one code path and a different state from another. |
| **Current state** | Vulnerable |
| **Required mitigation** | (1) Create a single canonical enum set for each entity, documented in one place. (2) Enforce valid transitions in a centralized state machine service. (3) Reject invalid transitions at the service layer, not just the controller. (4) Remove ghost states or document the transitions that set them. Reference: [[Platform Logical Audit - 2026-05-24]] Findings 5, 9, 20, 21. |
---
## 6. Risk Summary Matrix
| ID | Threat | Risk Level | Current State | Mitigation Owner | Reference |
|---|---|---|---|---|---|
| T01 | Fake payment proof (Web3) | Critical | Vulnerable | Backend / Payment | Task 2 subtask: Web3 verification hardening |
| T02 | Webhook replay attack | High | Partially mitigated | Backend / Payment | Task 2 subtask: Webhook security spec |
| T03 | Arbitrary Socket.IO room join | High | Vulnerable | Backend / Realtime | Task 2 subtask: Realtime authorization spec |
| T04 | Stolen token reuse | Critical | Partially mitigated | Backend / Auth | Task 2 subtask: Session architecture |
| T05 | Double payout / double release | Critical | Partially mitigated | Backend / Payment | Task 2 subtask: Funds ledger specification |
| T06 | Dispute bypass (release during dispute) | Critical | Vulnerable | Backend / Payment | Task 2 subtask: Escrow state machine |
| T07 | Email / registration abuse | Medium | Vulnerable | Backend / Auth | Task 2 subtask: Rate limiting |
| T08 | AI cost abuse | High | Vulnerable | Backend / AI | Task 2 subtask: Rate limiting + auth |
| T09 | Admin privilege escalation | High | Partially mitigated | Backend / Auth | Task 2 subtask: Authorization matrix |
| T10 | Passkey / WebAuthn bypass | High | Vulnerable | Backend / Auth | Task 2 subtask: Disable or fix passkeys |
| T11 | Web3 payment with wrong data (unauthenticated endpoints) | Critical | Vulnerable | Backend / Payment | Task 2 subtask: Auth on all financial endpoints |
| T12 | Rate limit bypass | High | Vulnerable | Backend / Infrastructure | Task 2 subtask: Rate limiting |
| T13 | XSS leading to token theft | High | Partially mitigated | Frontend / Backend | Task 2 subtask: Session architecture |
| T14 | npm supply-chain compromise | High | Partially mitigated | DevOps / Backend | Task 2 subtask: Supply-chain policy |
| T15 | CSRF on state-changing endpoints | Low | Mitigated | Backend / Auth | Monitor when cookie migration occurs |
| T16 | Deep-link / parameter tampering (Telegram) | Medium | Partially mitigated | Backend / Telegram | Task 2 subtask: Telegram initData verification |
| T17 | Payment provider / RPC outage | Medium | Partially mitigated | DevOps / Backend | Task 2 subtask: Operational runbooks |
| T18 | Insider fund manipulation | Critical | Vulnerable | Security / Operations | Task 2 subtask: Multi-sig wallet + ledger |
| T19 | Seller price manipulation post-acceptance | Medium | Vulnerable | Backend / Marketplace | Task 2 subtask: Offer status enforcement |
| T20 | Delivery confirmation brute force | Medium | Vulnerable | Backend / Marketplace | Task 2 subtask: Rate limiting + code entropy |
| T21 | Unauthenticated data exfiltration | High | Vulnerable | Backend / All | Task 2 subtask: Auth on all data endpoints |
| T22 | Verification code leakage via logs | Medium | Vulnerable | Backend / Auth | Immediate: remove log statements |
| T23 | Payment state machine inconsistency | High | Vulnerable | Backend / Payment | Task 2 subtask: Canonical state machine |
### Risk distribution
| Risk Level | Count | Threat IDs |
|---|---|---|
| Critical | 6 | T01, T04, T05, T06, T11, T18 |
| High | 10 | T02, T03, T08, T09, T10, T12, T13, T14, T21, T23 |
| Medium | 6 | T07, T16, T17, T19, T20, T22 |
| Low | 1 | T15 |
| **Total** | **23** | |
---
## 7. Assumptions and Constraints
### 7.1 In scope
- The Express 5 backend API server and all routes documented in [[Backend Architecture]].
- The Socket.IO realtime layer as documented in [[Real-time Layer]].
- The SHKeeper payment integration (pay-in webhook, payout API).
- The Web3/direct-wallet payment path (on-chain verification).
- The escrow state machine and fund custody model.
- The dispute lifecycle and its interaction with escrow.
- Authentication flows (email/password, Google OAuth, passkey/WebAuthn).
- Admin operations surface (dashboard, manual payouts, user management).
- Frontend token storage and session management.
- Infrastructure: single Docker host, CloudFlare/Nginx proxy, Watchtower deployment.
- npm supply chain for both frontend and backend.
- Telegram Mini App integration surface.
### 7.2 Out of scope (for this iteration)
- Physical security of hosting infrastructure.
- Social engineering attacks against platform operators (addressed partially by insider threat mitigations).
- Cryptographic weaknesses in blockchain protocols (BSC, Ethereum assumed secure).
- SHKeeper application internals (assumed to be a trusted component operated by the platform).
- Third-party email provider security (SMTP provider assumed secure).
- Client-side malware on user devices.
- Regulatory compliance (KYC/AML requirements not covered here).
- DNS hijacking (mitigated by CloudFlare proxy assumption).
### 7.3 Assumptions
1. **Hosting is a single Docker host** behind CloudFlare with Nginx. Internal service-to-service communication (MongoDB, Redis) is on localhost without TLS. This is acceptable for a single-host deployment but must be revisited if the architecture scales to multiple nodes.
2. **CloudFlare provides DDoS mitigation and TLS termination.** The threat model assumes HTTPS is enforced externally and the Nginx-to-Express hop is HTTP on localhost.
3. **SHKeeper is self-hosted and under platform control.** The webhook secret is shared only between the backend and the SHKeeper instance. If SHKeeper is compromised, webhook integrity is lost.
4. **MongoDB and Redis are not exposed to the internet.** They are bound to localhost on the Docker host. If this assumption is violated (e.g., misconfigured Docker network), the entire data layer is exposed.
5. **The escrow wallet private key is stored securely by the platform operator.** The threat model assumes the key is not in the codebase or env files. If this assumption is wrong, the exposure is total.
6. **Admin accounts are created via bootstrap seed and manually.** There is no self-service admin creation flow. The seed admin credentials must be rotated before any public launch.
7. **The frontend is a Next.js application served from the same origin or a CORS-allowlisted origin.** Cross-origin access is restricted by the CORS configuration. This assumption must hold for CSRF protection via `Authorization` header to be effective.
### 7.4 Constraints
1. **Single-host deployment limits defense-in-depth.** Network segmentation between services is not possible on a single host.
2. **No background job queue.** All async work is inline with request handlers or uses `setTimeout`/`setInterval`. This limits the ability to implement reliable retry, reconciliation, and scheduled operations.
3. **No external penetration testing has been performed.** All findings are based on code review and documentation analysis.
4. **Documentation and implementation may diverge.** Several findings in the [[Platform Logical Audit - 2026-05-24]] show that documentation describes security controls that may not be implemented in code. This threat model is based on documented behavior and known gaps; the actual attack surface may be larger.
5. **The platform is in pre-launch state.** Many security controls are documented as recommended but not yet implemented. This threat model reflects the current state, not the target state.
---
## Appendix A -- STRIDE Category Reference
| Category | Description |
|---|---|
| **S** -- Spoofing | Pretending to be another user or system |
| **T** -- Tampering | Modifying data or code |
| **R** -- Repudiation | Denying having performed an action |
| **I** -- Information disclosure | Exposing data to unauthorized parties |
| **D** -- Denial of service | Making a system unavailable |
| **E** -- Elevation of privilege | Gaining access beyond authorized level |
## Appendix B -- Threat-to-Mitigation Traceability
The following remediation documents (from the recommended documentation set in [[Backend Stack Security and Refactor Assessment - 2026-05-24]]) address specific threats:
| Remediation Document | Threats Addressed |
|---|---|
| Funds Ledger Specification | T05, T18, T23 |
| Escrow State Machine | T06, T19, T23 |
| Authorization Matrix | T09, T21 |
| Webhook Security Spec | T02 |
| Session and Auth Architecture | T04, T10, T13, T22 |
| Realtime Authorization Spec | T03 |
| Payment Provider Adapter Spec | T01, T11, T17 |
| Secure Build and Supply-Chain Policy | T14 |
| Operational Runbooks | T17 |
---
*This document was created on 2026-05-24 based on cross-referencing the platform audit documents, architecture documentation, and flow specifications. It must be reviewed and updated when: new payment providers are added, the escrow model changes, new authentication methods are introduced, the infrastructure topology changes, or significant new features are shipped.*