Files
ProxLedger/docs/sicherheit.md
T
florian.wildeandClaude Opus 5 7775b6211f feat(security): Haertung nach OWASP Top 10 (#024)
Drei echte Luecken gefunden und geschlossen:

1. Die 27 Server Actions verliessen sich allein auf proxy.ts. Jede prueft die
   Sitzung jetzt selbst und schlaegt gegen die Datenbank nach, ob das Konto
   noch gilt - damit lassen sich Sitzungen ueberhaupt erst widerrufen.
2. Der Anmelde-Endpunkt prueft die Herkunft des Formulars; vorher war
   Login-CSRF moeglich.
3. Content-Security-Policy mit Nonce je Anfrage und strict-dynamic.

Dazu: npm audit ist ohne Befund (mysql2 und deepmerge-ts per overrides
angehoben statt Prisma zurueckzustufen), eine Benutzerverwaltung fuer die
Kommandozeile und docs/sicherheit.md mit allem, was bewusst offen bleibt -
keine Rollen, keine Zwei-Faktor-Anmeldung.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 23:45:35 +02:00

5.4 KiB
Raw Permalink Blame History

Sicherheit

Dieses Dokument hält fest, was ProxLedger gegen die üblichen Angriffe tut – und was es nicht tut. Die Gliederung folgt den OWASP Top 10 (2021).

A01 – Fehlerhafte Zugriffskontrolle

  • src/proxy.ts schickt jede Anfrage ohne gültige Sitzung zur Anmeldung. Öffentlich sind nur die Anmeldung, die Agent-API (eigene Tokens), die Gesundheitsprüfung, die beiden Agent-Skripte und die Symbole.
  • Jede Server Action prüft zusätzlich selbst (sitzungErforderlich()). Server Actions sind eigene Endpunkte; eine einzige Schutzschicht davor wäre zu wenig, falls der Musterausdruck im Proxy je danebengreift.
  • Die Prüfung geht in die Datenbank: Ein gesperrtes Konto oder ein Widerruf beendet laufende Sitzungen sofort (npm run benutzer -- sperren <email>), statt bis zum Ablauf des Tokens zu warten.
  • Die Anmeldung prüft den Origin, damit keine fremde Seite jemanden unbemerkt in ein untergeschobenes Konto anmeldet (Login-CSRF). Für Server Actions erledigt das Next.js selbst.

Nicht vorhanden: Rollen. Wer angemeldet ist, darf alles. Für ein Werkzeug, das ein Systemhaus intern betreibt, ist das vertretbar – für mehrere Mandanten wäre es das nicht.

A02 – Kryptografische Fehler

  • Passwörter: argon2id (@node-rs/argon2), keine eigene Krypto.
  • Sitzungen: signiertes JWT (HS256) aus APP_SECRET, 12 Stunden gültig, Cookie httpOnly, sameSite=lax und secure, sobald APP_URL auf https:// steht.
  • Zugangsdaten für SMTP und Webhooks liegen AES-256-GCM-verschlüsselt in der Datenbank (src/lib/geheimnisse.ts); der Schlüssel wird aus APP_SECRET abgeleitet.
  • Agent- und Enrollment-Tokens werden nur als SHA-256-Hash gespeichert. Das ist hier richtig und nicht zu schwach: Die Tokens sind 32 Byte Zufall, es gibt nichts zu raten.

Wichtig: APP_SECRET ist der Generalschlüssel. Geht er verloren, sind alle Sitzungen ungültig und die gespeicherten Zugangsdaten unlesbar. Er gehört in die Sicherung.

A03 – Injektion

  • Datenbankzugriffe ausschließlich über Prisma mit gebundenen Werten; kein $queryRawUnsafe im Projekt.
  • Ausgaben maskiert React selbst.
  • Zusätzlich greift eine Content-Security-Policy mit frischem Nonce je Anfrage und strict-dynamic (src/proxy.ts). Eingeschleustes <script> – etwa über einen manipulierten Hostnamen aus einer Proxmox-Ausgabe – läuft damit nicht.
  • Der Agent baut sein JSON mit einer eigenen Maskierung; Sonderzeichen aus Proxmox-Ausgaben sind als Testfall hinterlegt.

A04 – Unsicheres Design

  • Push-Modell: Das Dashboard braucht keinen Zugang zu den Proxmox-Hosts. Es gibt keine gespeicherten root-Zugänge, die abhandenkommen könnten – die Hosts melden von sich aus.
  • Der Agent sendet nur Statusangaben (Subscription, Version, Sockel, IPs). Keine Gastliste, keine Daten.
  • Enrollment-Tokens sind befristet und in der Zahl der Nutzungen begrenzt.

A05 – Sicherheitsrelevante Fehlkonfiguration

  • Kopfzeilen: X-Content-Type-Options, X-Frame-Options: DENY, Referrer-Policy, Permissions-Policy, Strict-Transport-Security (next.config.ts) sowie die CSP aus dem Proxy.
  • Die Anwendung startet nicht mit zu kurzem APP_SECRET.
  • Im Container läuft die Anwendung als unprivilegierter Benutzer; Anhänge werden mit Rechten 640 abgelegt.
  • Fehlermeldungen nennen keine Einzelheiten: Bei der Anmeldung steht dieselbe Meldung für „Adresse unbekannt“ und „Passwort falsch“.

A06 – Verwundbare Komponenten

  • npm audit --omit=dev meldet keine bekannten Schwachstellen (Stand 18.09.2026).
  • Zwei Fassungen werden über overrides angehoben: mysql2 und deepmerge-ts kommen als Beiwerk von Prisma mit, obwohl wir Postgres nutzen. Ohne die Anhebung bliebe nur ein Rückschritt auf Prisma 6.
  • Die CI prüft bei jedem Push (#019).

A07 – Fehler bei Identifizierung und Authentifizierung

  • Ratenbremse: fünf Anmeldeversuche je Kennung und Viertelstunde.
  • Anmeldung wahlweise per Passwort oder OIDC (authentik, mit PKCE und state).
  • Jede Anmeldung – erfolgreich oder nicht – landet im Audit-Log.

Nicht vorhanden: Zwei-Faktor-Anmeldung. Wer sie braucht, stellt OIDC davor und schaltet die Passwortanmeldung ab.

A08 – Datenintegrität

  • Der Agent prüft vor dem Selbstaktualisieren, ob die geladene Datei wie ein Bash-Skript beginnt.
  • Webhooks werden mit HMAC-SHA256 signiert, damit die Gegenstelle die Herkunft prüfen kann.
  • Hochgeladene Rechnungen werden nicht dem gemeldeten Typ nach beurteilt, sondern am Dateianfang geprüft (%PDF-), auf 10 MB begrenzt und unter ihrer Prüfsumme abgelegt.

A09 – Protokollierung

  • Audit-Log für Anmeldungen, Änderungen an Kunden, Rechnungen, Tokens, Kanälen und Warnregeln – einsehbar unter Einstellungen → Protokoll.
  • Der Agent protokolliert auf dem Host über logger ins Journal.

A10 – Server-Side Request Forgery

  • Die einzige ausgehende Verbindung, die sich einstellen lässt, ist der Benachrichtigungs-Webhook. Er wird bewusst von einem Angemeldeten eingetragen und ist damit kein fremdgesteuerter Aufruf.
  • Sonst ruft das Dashboard keine Adressen auf, die aus Daten stammen.

Was ein Betreiber selbst tun muss

  1. HTTPS davorschalten (Beispiele unter deploy/reverse-proxy/) und APP_URL auf https:// setzen.
  2. APP_SECRET und die Datenbank sichern (deploy/backup.sh).
  3. Das Dashboard nicht ins offene Internet stellen, wenn es nicht sein muss. Die Agent-API muss erreichbar sein, die Oberfläche nicht.
  4. Enrollment-Tokens nach dem Ausrollen widerrufen.