cms.c2sgmbh/docs/anleitungen/SECURITY.md
Martin Porwoll 63b97c14f2 feat(security): enhance CSRF, IP allowlist, and rate limiter with strict production checks
- CSRF: Require CSRF_SECRET in production, throw error on missing secret
- IP Allowlist: TRUST_PROXY must be explicitly set to 'true' for proxy headers
- Rate Limiter: Add proper proxy trust handling for client IP detection
- Login: Add browser form redirect support with safe URL validation
- Add custom admin login page with styled form
- Update CLAUDE.md with TRUST_PROXY documentation
- Update tests for new security behavior

BREAKING: Server will not start in production without CSRF_SECRET or PAYLOAD_SECRET

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 05:06:15 +00:00

8.3 KiB

Security-Richtlinien - Payload CMS Multi-Tenant

Letzte Aktualisierung: 17.12.2025

Übersicht

Dieses Dokument beschreibt die implementierten Sicherheitsmaßnahmen für das Payload CMS Multi-Tenant-Projekt.


Security-Module

Alle Security-Funktionen befinden sich in src/lib/security/:

Modul Datei Zweck
Rate Limiter rate-limiter.ts Schutz vor API-Missbrauch
IP Allowlist ip-allowlist.ts IP-basierte Zugriffskontrolle
CSRF Protection csrf.ts Cross-Site Request Forgery Schutz
Data Masking data-masking.ts Sensitive Daten in Logs maskieren

Rate Limiter

Vordefinierte Limiter:

Name Limit Fenster Verwendung
publicApiLimiter 60 Requests 1 Minute Öffentliche API-Endpunkte
authLimiter 5 Requests 15 Minuten Login-Versuche
emailLimiter 10 Requests 1 Minute E-Mail-Versand
searchLimiter 30 Requests 1 Minute Suche & Posts-API
formLimiter 5 Requests 10 Minuten Formular-Submissions

Verwendung:

import { searchLimiter, rateLimitHeaders } from '@/lib/security'

export async function GET(req: NextRequest) {
  const ip = getClientIp(req.headers)
  const rateLimit = await searchLimiter.check(ip)

  if (!rateLimit.allowed) {
    return NextResponse.json(
      { error: 'Too many requests' },
      {
        status: 429,
        headers: rateLimitHeaders(rateLimit, 30)
      }
    )
  }
  // ...
}

Redis-Support:

  • Automatischer Fallback auf In-Memory-Store wenn Redis nicht verfügbar
  • Für verteilte Systeme: Redis via REDIS_URL konfigurieren

IP Allowlist/Blocklist

WICHTIG: TRUST_PROXY Konfiguration

Wenn die Anwendung hinter einem Reverse-Proxy (Caddy, Nginx, etc.) läuft, muss TRUST_PROXY=true gesetzt werden. Ohne diese Einstellung werden X-Forwarded-For und X-Real-IP Header ignoriert, um IP-Spoofing zu verhindern.

# In .env setzen wenn hinter Reverse-Proxy:
TRUST_PROXY=true

Ohne TRUST_PROXY=true wird für alle Requests direct-connection als IP verwendet, was Rate-Limiting und IP-Allowlists/-Blocklists ineffektiv macht.

Environment-Variablen:

Variable Zweck Format
TRUST_PROXY Proxy-Header vertrauen true oder leer
BLOCKED_IPS Globale Blocklist IP, CIDR, Wildcard
SEND_EMAIL_ALLOWED_IPS E-Mail-Endpoint Allowlist IP, CIDR, Wildcard
ADMIN_ALLOWED_IPS Admin-Panel Allowlist IP, CIDR, Wildcard
WEBHOOK_ALLOWED_IPS Webhook-Endpoint Allowlist IP, CIDR, Wildcard

Unterstützte Formate:

# Einzelne IP
BLOCKED_IPS=192.168.1.100

# Mehrere IPs
BLOCKED_IPS=192.168.1.100,10.0.0.50

# CIDR-Notation
BLOCKED_IPS=10.0.0.0/24

# Wildcard
BLOCKED_IPS=192.168.*

# Kombiniert
BLOCKED_IPS=10.0.0.0/8,192.168.1.50,172.16.*

Verwendung:

import { validateIpAccess } from '@/lib/security'

const ipCheck = validateIpAccess(req, 'sendEmail')
if (!ipCheck.allowed) {
  return NextResponse.json(
    { error: 'Access denied', reason: ipCheck.reason },
    { status: 403 }
  )
}

CSRF Protection

Pattern: Double Submit Cookie

Token-Endpoint: GET /api/csrf-token

WICHTIG: CSRF_SECRET in Production

In Production muss entweder CSRF_SECRET oder PAYLOAD_SECRET konfiguriert sein. Ohne Secret startet der Server nicht. Ein vorhersagbares Secret würde Angreifern erlauben, gültige CSRF-Tokens offline zu generieren.

# In .env setzen:
CSRF_SECRET=mindestens-32-zeichen-langes-geheimnis
# ODER PAYLOAD_SECRET wird als Fallback verwendet

Frontend-Integration:

// 1. Token abrufen
const res = await fetch('/api/csrf-token', { credentials: 'include' })
const { csrfToken } = await res.json()

// 2. Bei Mutations mitschicken
await fetch('/api/send-email', {
  method: 'POST',
  credentials: 'include',
  headers: {
    'Content-Type': 'application/json',
    'x-csrf-token': csrfToken,
  },
  body: JSON.stringify({ /* ... */ })
})

Server-seitige Validierung:

import { validateCsrf } from '@/lib/security'

const csrf = validateCsrf(req)
if (!csrf.valid) {
  return NextResponse.json(
    { error: 'CSRF validation failed' },
    { status: 403 }
  )
}

Bypass für Server-to-Server:

  • Requests ohne Origin/Referer Header werden als Server-to-Server behandelt
  • API-Key-basierte Authentifizierung umgeht CSRF-Check

Data Masking

Automatisch maskierte Felder:

  • password, passwd, pwd
  • token, accessToken, refreshToken
  • apiKey, api_key
  • secret, clientSecret
  • credentials
  • privateKey, private_key
  • smtpPassword, smtp_pass

Verwendung:

import { maskObject, createSafeLogger } from '@/lib/security'

// Objekte maskieren
const safeData = maskObject(sensitiveObject)

// Safe Logger verwenden
const logger = createSafeLogger('MyModule')
logger.info('User action', { password: 'secret' })
// Output: { password: '[REDACTED]' }

Pattern-Erkennung:

  • JWT Tokens: Header bleibt, Payload/Signature maskiert
  • Connection Strings: Passwort maskiert
  • Private Keys: Komplett ersetzt

Pre-Commit Hook

Secret Detection bei jedem Commit via scripts/detect-secrets.sh:

Installation:

ln -sf ../../scripts/detect-secrets.sh .git/hooks/pre-commit

Erkannte Patterns:

  • API Keys und Tokens
  • AWS Credentials
  • Private Keys
  • Passwörter in Code
  • SMTP Credentials
  • Database Connection Strings
  • JWT Tokens
  • Webhook URLs (Slack, Discord)
  • GitHub/SendGrid/Stripe Tokens

Ignorierte Dateien:

  • *.min.js, *.min.css
  • package-lock.json, pnpm-lock.yaml
  • *.md, *.txt
  • *.example, *.sample
  • *.spec.ts, *.test.ts (Test-Dateien)

CI/CD Security

.github/workflows/security.yml:

Job Prüfung
secrets Gitleaks Secret Scanning
dependencies npm audit, Dependency Check
codeql Static Code Analysis
security-tests 177 Security Unit & Integration Tests

Test Suite

Ausführung:

# Alle Security-Tests
pnpm test:security

# Nur Unit-Tests
pnpm test:unit

Abdeckung:

Test-Datei Tests Bereich
rate-limiter.unit.spec.ts 24 Limiter, Tracking, Reset, TRUST_PROXY
csrf.unit.spec.ts 34 Token, Validierung, Origin
ip-allowlist.unit.spec.ts 35 CIDR, Wildcards, Endpoints, TRUST_PROXY
data-masking.unit.spec.ts 41 Felder, Patterns, Rekursion
security-api.int.spec.ts 33 API-Integration

Empfehlungen

Production Checklist

  • TRUST_PROXY=true setzen (Pflicht hinter Reverse-Proxy wie Caddy)
  • CSRF_SECRET oder PAYLOAD_SECRET setzen (Server startet nicht ohne)
  • Alle BLOCKED_IPS für bekannte Angreifer setzen
  • SEND_EMAIL_ALLOWED_IPS auf vertrauenswürdige IPs beschränken
  • ADMIN_ALLOWED_IPS auf Office/VPN-IPs setzen
  • Redis für verteiltes Rate Limiting konfigurieren
  • Pre-Commit Hook aktivieren

Monitoring

  • Rate Limit Violations loggen (429 Responses)
  • CSRF Validation Failures überwachen
  • Blocked IP Attempts tracken
  • Login-Fehlversuche (authLimiter) alertieren

Custom Login Route

Das Admin Panel verwendet eine Custom Login Route (src/app/(payload)/api/users/login/route.ts) mit folgenden Features:

  • Audit-Logging: Jeder Login-Versuch wird in AuditLogs protokolliert
  • Rate-Limiting: 5 Versuche pro 15 Minuten (authLimiter)
  • Content-Type Support:
    • JSON (application/json)
    • FormData mit _payload JSON-Feld (Payload Admin Panel Format)
    • Standard FormData (multipart/form-data)
    • URL-encoded (application/x-www-form-urlencoded)

Sicherheitsaspekte:

  • Passwort wird nie in Logs/Responses exponiert
  • Fehlgeschlagene Login-Versuche werden mit IP und User-Agent geloggt
  • Rate-Limiting verhindert Brute-Force-Angriffe

Änderungshistorie

Datum Änderung
17.12.2025 Security-Audit Fixes: TRUST_PROXY für IP-Header-Spoofing, CSRF_SECRET Pflicht in Production, IP-Allowlist Startup-Warnungen, Tests auf 177 erweitert
09.12.2025 Custom Login Route Dokumentation, multipart/form-data _payload Support
08.12.2025 Security Test Suite (143 Tests)
07.12.2025 Rate Limiter, CSRF, IP Allowlist, Data Masking
07.12.2025 Pre-Commit Hook, GitHub Actions Workflow