feat(privat/CV): sync to latest upstream (cv-upstream/main, 30f9608b)

This commit is contained in:
2026-07-07 15:20:55 +02:00
parent 8ac664d33d
commit a4efabbc60
417 changed files with 48564 additions and 712 deletions
@@ -0,0 +1,90 @@
# 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-limit` in `server/middleware/rateLimiter.js` mit 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.js` mit 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 in `useFormSubmit`. 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:
```js
res.json({ ok: true, download_url: `/resources/${resource_id}.pdf` })
```
- **Risiko:** `resource_id` ist ungeprüft. Werte wie `../../etc/passwd%00` oder `../../assets/secret` könnten je nach Auslieferung manipulierte Pfade erzeugen.
- **Fix (umgesetzt):** `resource_id` wird strikt gegen `^[a-z0-9]+(-[a-z0-9]+)*$` validiert; ungültige IDs → 400. Verifiziert: `../../etc/passwd` abgelehnt, 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 `marked` als HTML gerendert (`body: marked(content)`) und via `dangerouslySetInnerHTML` (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:** `.env` und `server/data/*.json` korrekt 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 `confirmed` markiert → 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 auf `nodemailer@^8`. Aktueller Stand: **0 Vulnerabilities**.
- Versionen sind aktuell gepinnt mit `^` (Caret) — für reproduzierbare Builds ist `package-lock.json` vorhanden und maßgeblich.
---
## Maßnahmen-Checkliste (priorisiert)
- [x] 🔴 `express-rate-limit` + `trust proxy` einbauen
- [x] 🔴 Honeypot + Zeitfalle in allen Formularen (serverseitig für alle Endpunkte aktiv)
- [x] 🔴 `resource_id`-Regex-Validierung
- [x] 🟡 `helmet` + `x-powered-by` deaktivieren
- [x] 🟡 `express.json({ limit: '10kb' })`
- [x] 🟢 `npm audit` ausfü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