Drei Rollen: Administrator, Techniker, Kunde. Jede der 28 Aktionen nennt das Recht, das sie verlangt; die Rechte stehen an einer Stelle. Kundenkonten haengen an genau einem Kunden und sehen nur dessen Hosts und Rechnungen - ohne Einkaufspreise und Marge. Die Einschraenkung sitzt in den Abfragen selbst, damit keine Seite vergessen kann, sie weiterzugeben; sechs Tests pruefen das. Dabei gefunden: Neu ueber OIDC angelegte Konten haetten die Vorgabe des Datenmodells geerbt - ADMIN. Jetzt gibt es dafuer eine Einstellung mit der Vorgabe Techniker. Passkeys (#028) als gleichwertiger, passwortloser Anmeldeweg neben Passwort und OIDC. Mehrere je Konto, Aufgabe in einem kurzlebigen Cookie, rpID aus der oeffentlichen Adresse. Ohne HTTPS sagt die Seite es, statt still zu scheitern. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6.2 KiB
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.tsschickt 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.
Rollen (#027): Administrator, Techniker und Kunde. Jede Aktion nennt das Recht, das sie verlangt;
die Rechte stehen an einer Stelle (src/server/auth/rollen.ts). Kundenkonten sind an einen Kunden
gebunden, und die Einschränkung sitzt in den Abfragen – nicht in der Anzeige. Einkaufspreise und Marge
werden für sie schon beim Abfragen entfernt, stehen also nicht im ausgelieferten HTML.
Weiterhin nicht vorhanden: Mandantenfähigkeit im eigentlichen Sinn. Mehrere Systemhäuser in einer Installation wären ein Umbau, kein Anbau.
A02 – Kryptografische Fehler
- Passwörter: argon2id (
@node-rs/argon2), keine eigene Krypto. - Sitzungen: signiertes JWT (HS256) aus
APP_SECRET, 12 Stunden gültig, CookiehttpOnly,sameSite=laxundsecure, sobaldAPP_URLaufhttps://steht. - Zugangsdaten für SMTP und Webhooks liegen AES-256-GCM-verschlüsselt in der Datenbank
(
src/lib/geheimnisse.ts); der Schlüssel wird ausAPP_SECRETabgeleitet. - 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
$queryRawUnsafeim 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=devmeldet keine bekannten Schwachstellen (Stand 18.09.2026).- Zwei Fassungen werden über
overridesangehoben:mysql2unddeepmerge-tskommen 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.
-
Passkeys (WebAuthn, #028) als dritter, gleichwertiger Weg. Passwortlos, an die Domäne gebunden, mehrere je Konto. Setzt HTTPS voraus (außer auf
localhost). -
Automatisch angelegte OIDC-Konten bekommen die eingestellte Rolle – Vorgabe Techniker, nicht Administrator.
Nicht vorhanden: klassische Zwei-Faktor-Anmeldung mit Einmalkennwort. Ein Passkey ersetzt beides in einem Schritt; wer einen zweiten Faktor zum Passwort erzwingen will, stellt OIDC davor.
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
loggerins 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
- HTTPS davorschalten (Beispiele unter
deploy/reverse-proxy/) undAPP_URLaufhttps://setzen. APP_SECRETund die Datenbank sichern (deploy/backup.sh).- Das Dashboard nicht ins offene Internet stellen, wenn es nicht sein muss. Die Agent-API muss erreichbar sein, die Oberfläche nicht.
- Enrollment-Tokens nach dem Ausrollen widerrufen.