Files
Rendezvous/docs/security/threat-model.md
T
2026-07-16 04:11:24 +02:00

97 lines
7.2 KiB
Markdown

# Rendezvous v1 threat model
Tracking: #2
## Scope and assets
This model covers the public HTTP API, public LiteNetLib-compatible UDP mediator,
operator API, client SDK, game host integration, reverse proxy, secret provider,
observability pipeline, and the proposed future shared store. Gameplay traffic
after direct connection and game-owned identity/admission systems are outside
the service boundary, but their handoff is in scope.
Assets include tenant isolation, service availability, signing and publisher
keys, lease and connection credentials, raw endpoints, unlisted share codes,
listing integrity, audit integrity, and the guarantee that Rendezvous does not
turn into a reflector or private-network probe.
## Actors and assumptions
- Anonymous Internet attackers can send arbitrary HTTP and UDP traffic, spoof
source addresses where their network permits it, scrape listings, and create
many identities or addresses.
- Malicious publishers possess credentials only for their assigned tenant and
may submit hostile metadata or attempt to target arbitrary endpoints.
- Malicious clients can obtain legitimate join credentials for sessions they can
see and may replay, race, mutate, or share those credentials.
- A compromised game client and its SDK are fully attacker-controlled. No
reusable secret in them is trustworthy.
- Operators are privileged but fallible. Their actions are authenticated,
constrained, and audited.
- The reverse proxy, secret provider, and build/release pipeline are trusted
dependencies. Their compromise is considered and mitigated but cannot be
completely contained by the application.
## Abuse paths and controls
```mermaid
flowchart TD
A["Attacker input"] --> H{"HTTP or UDP?"}
H -->|HTTP| V["Authenticate when required; validate tenant, schema, size, and rate"]
H -->|UDP| U["Parse bounded datagram; validate capability before response"]
V --> S{"Allowed and in quota?"}
U --> E{"Capability valid, fresh, scoped, unused, and endpoint observed?"}
S -->|No| R["Stable bounded rejection"]
E -->|No| D["Silent drop + bounded aggregate metric"]
S -->|Yes| State["Atomic ephemeral state transition"]
E -->|Yes| State
State --> O["Allowlisted audit event; no secrets/endpoints"]
```
| Threat | Example | Required prevention/detection | Planned evidence |
| --- | --- | --- | --- |
| Spoofing and reflection | Forged UDP source causes traffic to a victim | No response before valid capability proof; send responses only to observed authenticated sources; at most two responses and <=2.0 verified byte amplification | Packet-level spoof/reflection tests and amplification accounting |
| Private-network probing | Publisher supplies `127.0.0.1`, link-local, or another victim as a same-LAN candidate | Accept only bounded private-unicast claims inside a scoped authenticated UDP contribution; reject prohibited ranges; disclose only to the opposite role in that attempt; bound SDK probes | Endpoint classification matrix and three-party adverse tests |
| Capability/ticket replay | Reuse a captured token to repeat introductions or connect | Short expiry, role/tenant/attempt scope, atomic one-time consumption, bounded skew, key rotation | Concurrent replay and post-expiry tests with golden vectors |
| Cross-tenant access | Game A browses, renews, or joins Game B | Server-derived principal scope on every lookup and atomic mutation; indistinguishable not-found response | Tenant isolation tests across every endpoint/store operation |
| Listing spam and scraping | Flood registrations or enumerate public sessions | Trust-mode quotas, per-principal/address limits, bounded pages/cursors, rate limits, aggregate alerts | Rate-limit, cursor-tamper, and sustained-load tests |
| Metadata injection | Control characters or markup attack logs/UI | UTF-8/schema/size validation; store as data; exclude values from audit; SDK does not render markup | Malformed Unicode/JSON corpus and TestClient safe-display tests |
| Credential theft | Secret appears in log, URL, metric, crash, or package | Credentials in headers/bodies only; allowlisted logging; secret-provider indirection; no credential metric labels | Log-capture tests, repository/package scans, rotation exercise |
| Parser/resource exhaustion | Oversized, nested, fragmented, or high-rate input | Fixed ceilings, bounded parsers/queues/concurrency, early rejection/drop, no input-sized logging | Fuzz/property corpus, allocation limits, overload tests |
| Stale or crashed host | Dead listing remains joinable | Both lease and recent authenticated presence required; atomic expiry; join rechecks freshness | Fake-clock lifecycle and join-race tests |
| Clock manipulation | Token accepted outside intended lifetime | Server-issued timestamps, monotonic elapsed-time for local expiry, <=30 s wall-clock skew | Boundary and clock-jump tests |
| Operator misuse | Unauthorized enumeration/revocation or secret exposure | Separate strong auth/network policy, least privilege, tenant scope, immutable audit, secrets never readable through API | Authorization matrix and audit completeness tests |
| Reverse-proxy confusion | Forged forwarded address bypasses limits | Trust forwarding headers only from allowlisted proxies; direct traffic uses socket peer | Forwarded-header spoof tests |
| Store race | Renew/revoke/expire/replay operations interleave | Compare-and-swap/transactional interfaces and deterministic outcomes | Parallel race tests with a fake clock |
| Dependency/supply-chain compromise | Malicious or drifting package/build output | Central pinning, lock files, reproducible builds, vulnerability review, signed release provenance | Locked clean restore, dependency audit, artifact verification |
| Availability attack | Valid-looking traffic fills CPU, memory, queues, logs | Layered quotas, bounded queues/tasks, graceful overload, readiness/drain, capacity alerts | Load/soak/resilience gates and forced saturation tests |
## Security invariants
The implementation and its tests must preserve these invariants:
1. No UDP response is sent to an endpoint that has not presented a valid scoped
capability from that observed endpoint.
2. No browse response contains an endpoint, secret, internal attempt ID, or
credential.
3. Every state lookup and mutation includes server-derived game/environment
scope; caller-supplied scope alone is never authoritative.
4. A listing is visible and joinable only while both lease and presence are
fresh at the atomic decision point.
5. A capability or ticket can cause at most one successful state transition for
its intended role and attempt.
6. Join authorization never bypasses host-owned final admission.
7. Input cannot create unbounded memory, work, response bytes, metric labels, or
log volume.
8. Raw endpoints and secrets never enter normal logs, traces, audit payloads, or
metric dimensions.
## Residual risk
Direct traversal cannot work through every NAT, firewall, carrier, or platform
policy. Rate limiting cannot eliminate distributed abuse. A compromised trusted
proxy, secret provider, operator identity, game grant issuer, or host credential
can act within its granted scope until detected and revoked. Unlisted share
codes can be disclosed by recipients. These risks are communicated as typed
outcomes and operational signals rather than hidden behind a success claim.