286fbfeb36
Closes #2
97 lines
7.2 KiB
Markdown
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.
|