Zum Inhalt springen

Sicherheit

Tocco-Mate verwaltet deine echten WISS-Zugangsdaten und deine Noten. Sicherheit ist daher ernst gemeint.

Alle /api/*-Routen brauchen einen Bearer-Token im Authorization-Header:

Terminal-Fenster
curl -H "Authorization: Bearer $API_TOKEN" http://localhost:3000/api/noten

Der Token wird beim ersten Start auto-generiert (32 zufällige Bytes) und in data/.api-token abgelegt.

?token=… ist nur auf /api/events erlaubt (EventSource kann keine Header setzen). Auf allen anderen Routen wird Query-Token abgewiesen — damit der Token nicht in Reverse-Proxy-Logs, Browser-History oder Referrer-Headern landet.

SchichtLimitLockout
Kurz10 Fehlversuche / 15 min15 min
Mittel50 Fehlversuche / 6 h6 h
SSE-spezifisch60 Fehlversuche / 15 min(toleriert EventSource-Reconnect-Storms)

Implementiert in src/ratelimits.js mit express-rate-limit.

  • Tocco-URLs nur via ENV-Variablen setzbar — kein UI-Zugriff
  • Push-Endpoints auf Whitelist beschränkt: FCM (Google), Mozilla Autopush, Apple, Windows Notification Service

Sensible Werte in data/settings.json (msPassword, telegramToken) werden mit AES-256-GCM verschlüsselt.

  • Master-Key: 32 zufällige Bytes, beim ersten Start auto-generiert in data/.master-key (Mode 0600)
  • Format-Versioning: enc:v1:<iv>:<ct>:<tag> — erlaubt künftige Algo-Wechsel
  • Lazy Migration: Bestands-Plaintext-Werte werden beim nächsten Save migriert

✅ Backup-Leaks (wenn .master-key separat gesichert) ✅ Volume-Snapshots ✅ Casual File-Sharing

❌ Shell-Access auf den laufenden Host (Master-Key liegt neben den Daten) ❌ Memory-Dumps ❌ Container-Escape

Für öffentliche Exposition immer Reverse-Proxy mit TLS davor.

tocco-mate.example.com {
reverse_proxy 127.0.0.1:3000
}

Caddy holt automatisch ein Let’s-Encrypt-Zertifikat.

server {
listen 443 ssl http2;
server_name tocco-mate.example.com;
ssl_certificate /etc/letsencrypt/live/tocco-mate.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tocco-mate.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE braucht Buffering aus
proxy_buffering off;
proxy_cache off;
}
}
labels:
- "traefik.enable=true"
- "traefik.http.routers.tocco.rule=Host(`tocco-mate.example.com`)"
- "traefik.http.routers.tocco.entrypoints=websecure"
- "traefik.http.routers.tocco.tls.certresolver=letsencrypt"
- "traefik.http.services.tocco.loadbalancer.server.port=3000"
SetupTRUST_PROXY
Direkt (kein Proxy)loopback
1 Reverse-Proxy davor1 (Default)
Cloudflare → nginx → app2
Niemalstrue (IP-Spoofing)

Das App-interne Encryption-Layer schützt settings.json gegen versehentliche data/-Leaks. Für echte Backup-Sicherheit das Backup zusätzlich verschlüsseln:

Terminal-Fenster
restic init --repo /backups/tocco-mate
restic -r /backups/tocco-mate backup ./data --exclude 'data/.master-key'
Terminal-Fenster
borg init --encryption=repokey /backups/tocco-mate
borg create /backups/tocco-mate::$(date +%Y%m%d) ./data --exclude '*/.master-key'
Terminal-Fenster
tar czf - data | gpg --symmetric --cipher-algo AES256 -o tocco-backup-$(date +%Y%m%d).tar.gz.gpg
DateiInhaltSensibel?
tocco.dbSQLite mit Noten + StundenplanPersönliche Daten
settings.jsonSettings (verschlüsselt: msPassword, telegramToken)Ja
.api-tokenAPI-Bearer-TokenJa
.master-keyAES-256-GCM Master-KeySehr
vapid.jsonWeb-Push VAPID-KeysJa
.weekly-detail-atWochen-Check-MarkerNein

data/ niemals veröffentlichen — auch nicht Read-Only.

Helmet-CSP mit script-src 'self' 'unsafe-inline'. Hintergrund: SvelteKit adapter-static prerendert Inline-Bootstrap-Skripte zur Build-Zeit. Migration zu strikter CSP ist als TODO im Code dokumentiert.