Security¶
Who is this page for?
Operators and security reviewers. It collects every enforcement boundary in one place. Credential usage is in Authentication; the scoring sandbox is in The DSL Strategy Engine.
Threat model in one paragraph¶
GAME is a multi-tenant scoring backend reachable by many API keys and OAuth identities. The properties it defends: (a) one tenant cannot read or mutate another’s data; (b) a flood of requests cannot exhaust the service or skew scoring; (c) user-supplied strategies cannot execute arbitrary code or run away with resources; and (d) a misconfiguration cannot silently downgrade these guarantees in production.
Authentication¶
Two credentials, resolved API-key-first then OAuth2, validated strictly
(RS256 JWTs with issuer/audience/expiry checks). The full mechanism, failure
codes, and the AdministratorGAME admin role are documented in
Authentication.
Abuse prevention & rate limiting¶
Sensitive write endpoints (point assignment, action recording) pass through
AbusePreventionService before doing work. It enforces, per request:
Limit |
Default |
Env var |
|---|---|---|
Short-window requests per API key |
120 / 60 s |
|
Short-window requests per IP |
240 / 60 s |
|
Short-window requests per external user |
60 / 60 s |
|
Daily quota per API key |
10 000 / day |
|
Window length |
60 s |
|
Over-limit requests get 429 with a descriptive detail. The whole subsystem
can be turned off with ABUSE_PREVENTION_ENABLED=false (not recommended in
production).
Counter backend¶
The counter store is pluggable via ABUSE_PREVENTION_BACKEND:
database(default) - increments a row inAbuseLimitCounter. Simple, but a hot row under load.redis- atomicINCR+EXPIREagainstREDIS_URL(~50 µs vs. ~5 ms for the Postgres UPDATE, and naturally shared across instances). Recommended for multi-replica deployments.
Trusted proxies (don’t let clients forge their IP)¶
Per-IP limits are only meaningful if the client IP can be trusted. When GAME
runs behind a reverse proxy/ingress, the real client IP arrives in
X-Forwarded-For / X-Real-IP - headers a client could otherwise forge to
dodge per-IP limits.
TRUSTED_PROXY_IPS is the gate. It is a comma-separated list of IPs/CIDRs
allowed to set forwarding headers:
Empty (default) - no proxy is trusted; forwarding headers are ignored and the socket peer is used. This is the secure default.
Set to your proxy/ingress IP(s) - only then are forwarding headers honored.
Malformed entries are rejected at startup, so a typo fails fast instead of silently trusting no one.
CORS¶
CORS origins come from BACKEND_CORS_ORIGINS (a plain comma-separated list,
not JSON). The middleware is only attached when origins are configured.
Two safety behaviors:
Wildcard is rejected in protected environments. With
ENV=prodorstage,BACKEND_CORS_ORIGINS=*raises at startup - a wildcard combined with credentialed requests would let any site act on the user’s behalf.CORS wraps the error handler. The middleware ordering (see Architecture) guarantees even a
500carries CORS headers, so the browser surfaces the real status instead of a bare “Network Error”.
Secrets & fail-fast configuration¶
In prod/stage the app refuses to boot when a security-critical
setting is missing or left at an insecure default:
Guard |
Boot blocks if… |
|---|---|
|
empty. (It previously defaulted to the literal string |
|
missing or equal to the shipped dev placeholder. |
|
unset. (Prevents prod/stage workloads from silently writing to a
database named |
|
set to |
These checks run once at import of app.core.config and turn a class of
“works in dev, leaks in prod” mistakes into loud startup failures.
Important
Manage secrets via the environment or a secret manager - never commit
real secrets, and don’t ship a .env with production values. See
Configuration Reference.
Strategy sandbox¶
User-authored DSL strategies are the largest attack surface and get their own
defenses: a validator that whitelists every node/op/field and bounds size, and
an interpreter with no eval/exec/getattr, frozen field access, and
a cancellable wall-clock timeout. See The DSL Strategy Engine.
Auditability¶
Every write stamps
apiKey_used/oauth_user_id(theBaseModelcontract), so any row’s origin is known.Endpoints emit structured audit logs (
AuditLogger) with correlation ids.Data exports are recorded in
ExportAuditLog.DSL runs are sampled into
StrategyExecutionLog.
Hardening checklist¶
✓ |
Item |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
One API key per integration/ |