# Sicherheit und DSGVO: was du sofort machen musst

Sicherheit ist langweilig, bis sie es nicht mehr ist. Wenn du heute einen Prototyp baust und morgen die ersten echten Nutzer hast, sind ein paar Dinge nicht verhandelbar. Andere kannst du später nachziehen.

Dieses File trennt klar: was SOFORT, was später.

## SOFORT (am Workshop-Tag)

### 1. EU-Hosting

DSGVO will, dass personenbezogene Daten in der EU bleiben oder in Ländern mit Angemessenheitsbeschluss. Praktisch: nimm einen Server in Deutschland oder EU.

Empfehlung:
- **Hetzner** (Falkenstein/Nürnberg): solide, günstig (ab 5 Euro/Monat), Standard im SAC-Stack.
- **Scaleway** (Paris): Französisch, kompetent.
- **OVH** (Frankreich): groß, etwas hakelig in der UI.

Nicht im Prototyp ohne extra Vertrag:
- AWS US, GCP US, Azure US (USA, Datenschutz unklar trotz Frameworks).
- Cloudflare R2 als Speicher (rechtlich grenzwertig, je nach Daten).
- Vercel (US-Hosting, Edge in EU aber Server-Locations konfigurieren nötig, AVV unterschreiben).

Wenn du Vercel oder Netlify nimmst, prüfe vorher das DPA (Data Processing Agreement) und konfiguriere die Region explizit auf Frankfurt oder Paris.

### 2. HTTPS überall

Kein HTTP. Niemand. Nirgendwo.

Dokploy mit Traefik macht das automatisch via Let's Encrypt. Vercel/Netlify auch. Wenn du selbst hostest: Caddy oder Traefik nehmen, die regeln HTTPS-Zertifikate automatisch.

Cookies dann mit `secure: true` setzen, sonst funktionieren sie nur über HTTPS.

### 3. Passwörter richtig hashen

Wenn du Passwörter speicherst: **Argon2id**, nichts anderes.

```ts
import argon2 from "argon2";
const hash = await argon2.hash(passwort, {
  type: argon2.argon2id,
  memoryCost: 19456,
  timeCost: 2,
  parallelism: 1,
});
```

Verboten:
- MD5, SHA1, SHA256 (zu schnell, daher knackbar).
- bcrypt (in Ordnung, aber schwächer als Argon2id).
- "Eigenes Salt-Schema". Du baust nichts, was schon existiert.

### 4. Keine Secrets im Code

API-Keys, DB-Passwörter, JWT-Secrets gehören in `.env`-Dateien, nicht in den Code.

```bash
# .env
DATABASE_URL="postgresql://..."
SESSION_SECRET="hier 32 zufaellige bytes"
RESEND_API_KEY="re_..."
```

Und in `.gitignore`:
```
.env
.env.local
.env*.local
```

Ein versehentlich committeter API-Key kostet dich Geld oder deine Datenbank. Niemand kontrolliert das, außer du.

Wenn doch passiert: Key sofort beim Anbieter rotieren, dann erst löschen im Git-Verlauf.

### 5. Rate-Limiting auf öffentlichen Endpoints

Jeder Endpoint, der ohne Login erreichbar ist (Login-Form, Magic-Link-Request, Public-API), muss rate-limited sein.

Pattern:
- Pro IP: maximal 20 Requests pro 15 Minuten.
- Pro Email beim Login: maximal 5 Versuche pro 15 Minuten.

Im Prototyp reicht eine In-Memory-Map. Bei Restart geht das Limit verloren, aber das ist im Prototyp ok. Ab MVP nimm Redis.

```ts
// simples In-Memory-Limit
const versuche = new Map<string, { count: number; reset: number }>();
function rateLimit(key: string, max: number, windowMs: number): boolean {
  const now = Date.now();
  const entry = versuche.get(key);
  if (!entry || entry.reset < now) {
    versuche.set(key, { count: 1, reset: now + windowMs });
    return true;
  }
  if (entry.count >= max) return false;
  entry.count++;
  return true;
}
```

### 6. Input validieren

Niemals Form-Eingaben direkt in die DB schreiben ohne Prüfung.

- Email-Felder: Format prüfen.
- String-Felder: Länge begrenzen (z.B. max 200 Zeichen für Namen).
- Zahl-Felder: Min und Max prüfen.
- Datum-Felder: Plausibilität prüfen (kein Tisch im Jahr 1990 reservierbar).

Tools: **Zod** ist Standard im TypeScript-Ökosystem, in 5 Minuten gelernt.

```ts
import { z } from "zod";
const schema = z.object({
  gastName: z.string().min(2).max(100),
  gastEmail: z.string().email(),
  startZeit: z.coerce.date(),
});
const data = schema.parse(formInput); // wirft, wenn ungültig
```

### 7. Datenschutzerklärung und Impressum

Wenn deine App live geht und Nutzer Daten eingeben, brauchst du:
- **Impressum** (Pflicht in Deutschland, §5 TMG)
- **Datenschutzerklärung** (Pflicht laut DSGVO Art. 13)

Generator: e-recht24.de oder anwalt.de. Kostenlose Generatoren reichen für den Anfang. Sobald du Geld verdienst: einmal von einem Anwalt prüfen lassen.

Speziell drin sein muss:
- Welche Daten du verarbeitest.
- Wozu du sie verarbeitest (Zweck).
- Wie lange du sie speicherst.
- Welche Auftragsverarbeiter du nutzt (Hetzner, Resend, etc.).

## DANACH (in den ersten Wochen)

### 8. Backups

Postgres-Backups, täglich, automatisch, an einen anderen Ort.

Optionen:
- `pg_dump` per Cronjob in einen S3-kompatiblen Speicher (Hetzner Storage Box, Backblaze B2).
- Managed Postgres mit eingebautem Backup (Neon, Supabase, Hetzner Managed Postgres).

Wichtig: einmal pro Quartal Restore testen. Backups, die nie restored wurden, existieren nicht.

### 9. Verschlüsselung at-Rest

Heißt: Daten auf der Festplatte sind verschlüsselt, falls jemand die Disk stiehlt.

- Hetzner-VPS: standardmäßig nicht verschlüsselt. Bei sensiblen Daten: LUKS-Verschlüsselung beim Setup aktivieren.
- Managed Postgres: oft schon verschlüsselt, im Dashboard prüfen.

Für die meisten Prototypen nicht kritisch. Wird kritisch, sobald du Gesundheitsdaten, Finanzdaten oder Kindern-Daten verarbeitest.

### 10. Sensible Felder zusätzlich verschlüsseln

Wenn du in der DB Dinge speicherst, die selbst bei einem DB-Leak nicht lesbar sein sollen (API-Keys von Kunden, OAuth-Tokens für Dritt-Services), verschlüssele sie per **AES-256-GCM** mit einem App-internen Key.

```ts
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";

function encrypt(plaintext: string, key: Buffer): string {
  const iv = randomBytes(12);
  const cipher = createCipheriv("aes-256-gcm", key, iv);
  const enc = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
  const tag = cipher.getAuthTag();
  return Buffer.concat([iv, tag, enc]).toString("base64");
}
```

Key in `.env` als Hex/Base64. Pro Credential ein eigener IV (random), Tag mitspeichern. So macht es Connector im SAC-Stack.

### 11. Login-Lockout

Nach mehreren fehlgeschlagenen Login-Versuchen das Konto temporär sperren. Pattern:

- 20 Fehlversuche pro IP pro 15 Minuten → IP sperren.
- 5 Fehlversuche pro Email pro 15 Minuten → Email sperren.

Sonst sind Brute-Force-Angriffe zu einfach.

### 12. Auftragsverarbeitungsverträge (AVV)

Jeder externe Dienst, der mit personenbezogenen Daten in Berührung kommt, braucht einen AVV. Praktisch:
- Hosting-Provider (Hetzner, Vercel)
- Mail-Provider (Resend, Mailgun)
- Analytics (wenn du Plausible oder Matomo selbst hostest: kein AVV nötig)
- Error-Tracking (Sentry: AVV existiert, herunterladen)

AVVs sind meist kostenlose PDFs auf der Anbieter-Webseite. Einmal unterschreiben, ablegen.

## NIE

| Aktion | Warum |
|---|---|
| Passwörter im Klartext speichern | Bei jedem Leak ist alles offen. |
| API-Keys ins Git committen | Bots scannen GitHub minütlich. |
| `eval()` auf User-Input | Code-Injection. |
| SQL-Queries mit String-Konkatenation | SQL-Injection. Prisma/ORMs schützen automatisch. |
| Sensible Daten ins Log schreiben | Logs landen oft an unerwarteten Orten. |
| Eigene Crypto-Algorithmen erfinden | Du machst es falsch. Garantiert. |

## Checkliste für den Workshop-Tag

- [ ] Server in der EU.
- [ ] HTTPS aktiv.
- [ ] `.env` in `.gitignore`.
- [ ] Passwörter (falls vorhanden) mit Argon2id gehasht.
- [ ] Login + Magic-Link rate-limited.
- [ ] Input mit Zod validiert.
- [ ] Impressum + Datenschutzerklärung als Platzhalter-Seiten angelegt (vor Go-Live richtig befüllen).

Wenn diese 7 Punkte stehen, bist du im Prototyp-Stand sicher genug.

## DSGVO-Tipps für Gründer

- Sammle nur, was du brauchst. Jedes Feld ist ein Risiko.
- Lösche, was du nicht mehr brauchst. Account-Löschung muss möglich sein.
- Schreibe in der Datenschutzerklärung exakt, was du machst. Nicht copy-paste von einer fremden Webseite.
- Bei Cookie-Banner: du brauchst nur einen, wenn du nicht-essenzielle Cookies setzt (Analytics, Tracking). Reine Session-Cookies sind ohne Banner ok.
- Bei Kontaktformularen: Hinweis "Daten werden zur Bearbeitung deiner Anfrage verarbeitet" reicht.

## Nächster Schritt

Geh die SOFORT-Liste durch, während du baust. Bevor du live gehst: Checkliste komplett, AVVs unterschrieben, Impressum aktiviert. Lies parallel webapp-deploy.md, da steht wie du das alles auf einen Server kriegst.
