feat(server): add security hardening for public hosting

- Add CSP, frame and referrer headers for served static files
- Enforce WebSocket origin check to prevent cross-site hijacking
- Trust X-Forwarded-For only when the peer is from a private proxy network
- Limit concurrent connections (500 total / 20 per IP) and rooms (300)
- Add ALLOWED_ORIGINS env var for additional WebSocket origins
- Document reverse proxy setup (NPM/NPMplus) in README
- Add scripts/security-test.mjs to verify origin and limit behavior
This commit is contained in:
Tronax 2026-08-16 13:16:20 +02:00
parent b506ffad55
commit 4ec0e483aa
Signed by: Tronax
SSH key fingerprint: SHA256:2pKKXDZucWvaF/GzXNz0FY53EAO1YDLN80bqS+TTz/o
3 changed files with 184 additions and 0 deletions

View file

@ -55,6 +55,29 @@ Danach läuft das komplette Spiel inkl. Online-Multiplayer unter **http://localh
| --- | --- | --- |
| `PORT` | `3001` | HTTP- und WebSocket-Port |
| `DIST_DIR` | `./dist` | Ordner mit den statischen Dateien |
| `ALLOWED_ORIGINS` | | Zusätzlich erlaubte WebSocket-Origins (Komma-Liste, z. B. `https://spiel.example.com`) |
**Öffentliches Hosting hinter Reverse Proxy (Nginx Proxy Manager / NPMplus):**
1. Container **nur im internen Netz** erreichbar machen (Port 3001 **nicht** am Router freigeben!), z. B.:
```yaml
# docker-compose.yml — gemeinsames Netzwerk mit dem Proxy
services:
trxtd:
build: .
restart: unless-stopped
expose: ["3001"] # intern, kein ports:-Mapping nach außen
# networks: proxy_net ...
```
2. In NPM/NPMplus einen **Proxy Host** anlegen:
- **Domain:** `spiel.example.com` (DNS-A-Record auf den Server)
- **Scheme:** `http` · **Forward Hostname:** Containername (gemeinsames Docker-Netz) oder Server-LAN-IP · **Port:** `3001`
- **Websockets Support: ✔ aktivieren** (zwingend erforderlich für Multiplayer!)
- Block Common Exploits: ✔ · Cache Assets: ✘ (der Server setzt eigene Cache-Header)
- **SSL-Tab:** Let's-Encrypt-Zertifikat anfordern, *Force SSL* + *HTTP/2* aktivieren
3. Fertig das Spiel (inkl. Coop/1v1 über `wss://`) läuft unter `https://spiel.example.com`.
**Serverseitige Härtung (aktiv):** CSP-/Frame-/Referrer-Header · Origin-Check gegen Cross-Site-WebSocket-Hijacking · `X-Forwarded-For`-Auswertung nur aus privaten Proxy-Netzen · max. 500 WebSocket-Verbindungen total / 20 pro IP · max. 300 Räume · Payload-Limit 4 KB · Rate-Limit 30 Aktionen/s · Schema-Validierung · Ping/Pong-Heartbeat · Raum-Timeout 30 min.
**Manuell ohne Docker im Produktionsmodus starten:**
```bash