6.0 KiB
Sicherheits-Audit — andreknie.de
Stand-Analyse des Backends (Express 5), des Frontends (Vite/React SPA) und der Deployment-Konfiguration (Docker + Caddy). Bewertung: 🔴 kritisch (vor Go-Live beheben) · 🟡 wichtig (zeitnah) · 🟢 ok / Hinweis
Zusammenfassung
Die Architektur ist grundsolide: Double-Opt-In für alle Formulare, keine Datenbank (Dateispeicher), keine Tracking-Cookies, Secrets über .env (korrekt aus Git ausgeschlossen), TLS + Security-Header über Caddy. Vor dem Livegang gibt es jedoch mehrere Lücken, vor allem fehlendes Rate-Limiting, keine Absicherung gegen automatisierte Spam-Bots und ein Path-Traversal-Risiko beim Resource-Download.
🔴 Kritisch — vor Go-Live beheben
1. Kein Rate-Limiting — ✅ BEHOBEN
Die Planung (Task 8.1) sah server/middleware/rateLimiter.js vor (contact 3/h, talk 3/h, newsletter 5/h, resource 10/h). Dieses Verzeichnis existiert nicht. Alle Endpunkte sind unbegrenzt aufrufbar.
- Risiko: Mail-Flooding (jede Anfrage löst eine E-Mail aus → Versand-Kontingent/Reputation gefährdet), Denial-of-Service, Befüllen der JSON-Dateien bis zur Plattenauslastung.
- Fix (umgesetzt):
express-rate-limitinserver/middleware/rateLimiter.jsmit Limits pro Endpunkt (contact/talk 3/h, newsletter 5/h, resource 10/h).app.set('trust proxy', 1)gesetzt für korrekte Client-IP hinter Caddy. Verifiziert: 4. Kontakt-Request → 429.
2. Kein Spam-/Bot-Schutz — ✅ BEHOBEN
Formulare haben keinerlei Schutz gegen automatisiertes Absenden (kein Honeypot, kein Captcha, keine Zeitfalle).
- Risiko: Da jede Übermittlung eine Bestätigungsmail an eine vom Absender frei wählbare Adresse auslöst, lässt sich die Seite als Spam-Schleuder / Backscatter-Quelle missbrauchen.
- Fix (umgesetzt):
server/middleware/antiSpam.jsmit Honeypot-Feld (company_website) + serverseitiger Zeitfalle (form_started_at, < 2 s = Bot). Erkannte Bots erhalten ein Fake-201 (lernen nicht, dass sie erkannt wurden). Frontend: verstecktes Honeypot-Feld im Kontaktformular, Zeitstempel automatisch inuseFormSubmit. Verifiziert: gefülltes Honeypot/Zeitfalle → kein Lead/keine Mail.
3. Path-Traversal beim Resource-Download — ✅ BEHOBEN
In resource-download.js wird die Download-URL direkt aus dem Input gebaut:
res.json({ ok: true, download_url: `/resources/${resource_id}.pdf` })
- Risiko:
resource_idist ungeprüft. Werte wie../../etc/passwd%00oder../../assets/secretkönnten je nach Auslieferung manipulierte Pfade erzeugen. - Fix (umgesetzt):
resource_idwird strikt gegen^[a-z0-9]+(-[a-z0-9]+)*$validiert; ungültige IDs → 400. Verifiziert:../../etc/passwdabgelehnt, kein Lead angelegt.
🟡 Wichtig — zeitnah
4. Fehlende Security-Header am Express-Server (helmet) — ✅ BEHOBEN
Caddy setzt zwar HSTS, X-Frame-Options etc. für statische Auslieferung. Die /api/*-Antworten kommen aber direkt aus Express.
- Fix (umgesetzt):
helmet()eingebunden;app.disable('x-powered-by')gesetzt.
5. Keine Body-Size-Begrenzung — ✅ BEHOBEN
- Fix (umgesetzt):
express.json({ limit: '10kb' }).
6. Race Condition / Datenverlust beim Datei-Speicher
loadTokens()/saveTokens(), subscribers.json, leads.json arbeiten mit synchronem Read-Modify-Write ohne Locking. Bei gleichzeitigen Requests kann ein Schreibvorgang den anderen überschreiben.
- Risiko: Verlorene Subscriber/Leads, korrupte JSON.
- Fix: Für geringe Last vertretbar, aber dokumentieren. Mittelfristig serialisieren (Write-Queue) oder auf SQLite umsteigen.
7. Content-Security-Policy fehlt
Weder Caddy noch helmet setzen aktuell eine CSP.
- Risiko: Erhöhte XSS-Auswirkung. React escaped zwar per Default, aber Kniepunkt-Inhalte werden über
markedals HTML gerendert (body: marked(content)) und viadangerouslySetInnerHTML(zu prüfen) eingebunden. - Fix: CSP in Caddy setzen (
default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src fonts.gstatic.com; ...). Markdown-Quellen sind selbst verfasst (geringes Risiko), aber CSP ist Defense-in-Depth.
8. CORS-Origin unflexibel
origin: 'https://andreknie.de' — www.-Variante würde abgelehnt. Prüfen, ob www.andreknie.de per Caddy auf die Apex-Domain umgeleitet wird (dann ok).
🟢 OK / positiv
- Secrets:
.envundserver/data/*.jsonkorrekt in.gitignore. Keine Hardcoded-Credentials im Code. - Double-Opt-In: Sauber umgesetzt, Tokens via
crypto.randomUUID(), Ablauf 24h/48h, stündliches Cleanup. - Token-Verbrauch: Tokens werden bei Bestätigung als
confirmedmarkiert → kein Replay. - Eingabevalidierung: Längen- und Formatprüfungen pro Feld vorhanden (Name, E-Mail, Nachricht).
- Keine Tracking-Cookies / keine Drittanbieter-Analytics (datenschutzfreundlich, deckt sich mit Datenschutzerklärung).
- TLS: Auto-HTTPS via Caddy/Let's Encrypt.
- E-Mail-Inhalte: Als
text(kein HTML) versendet → kein HTML-Injection-Vektor in Mails. - Fehler-Handler: Gibt generische Meldung aus, leakt keine Stacktraces an den Client.
Abhängigkeits-Hinweise
npm audit: nodemailer v6 hatte eine high-severity Vulnerability (GHSA-mm7p-fcc7-pg87 / GHSA-rcmh-qjqh-p98v). Behoben durch Upgrade aufnodemailer@^8. Aktueller Stand: 0 Vulnerabilities.- Versionen sind aktuell gepinnt mit
^(Caret) — für reproduzierbare Builds istpackage-lock.jsonvorhanden und maßgeblich.
Maßnahmen-Checkliste (priorisiert)
- 🔴
express-rate-limit+trust proxyeinbauen - 🔴 Honeypot + Zeitfalle in allen Formularen (serverseitig für alle Endpunkte aktiv)
- 🔴
resource_id-Regex-Validierung - 🟡
helmet+x-powered-bydeaktivieren - 🟡
express.json({ limit: '10kb' }) - 🟢
npm auditausführen → nodemailer@8, 0 Vulnerabilities - 🟡 CSP in Caddyfile ergänzen
- 🟡 Schreibzugriffe auf JSON serialisieren (oder SQLite-Migration planen)
- 🟢
www.→Apex-Redirect in Caddy prüfen