feat(privat/CV): sync to latest upstream (cv-upstream/main, 30f9608b)
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user