- Remove obsolete instruction documents (PROMPT_*.md, SECURITY_FIXES.md) - Update CLAUDE.md with security features, test suite, audit logs - Merge Techstack_Dokumentation into INFRASTRUCTURE.md - Update SECURITY.md with custom login route documentation - Add changelog to TODO.md - Update email service and data masking for SMTP error handling - Extend test coverage for CSRF and data masking 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
7 KiB
Security-Richtlinien - Payload CMS Multi-Tenant
Letzte Aktualisierung: 09.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_URLkonfigurieren
IP Allowlist/Blocklist
Environment-Variablen:
| Variable | Zweck | Format |
|---|---|---|
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
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/RefererHeader 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.csspackage-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 |
143 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 |
21 | Limiter, Tracking, Reset |
csrf.unit.spec.ts |
34 | Token, Validierung, Origin |
ip-allowlist.unit.spec.ts |
29 | CIDR, Wildcards, Endpoints |
data-masking.unit.spec.ts |
41 | Felder, Patterns, Rekursion |
security-api.int.spec.ts |
18 | API-Integration |
Empfehlungen
Production Checklist
- Alle
BLOCKED_IPSfür bekannte Angreifer setzen SEND_EMAIL_ALLOWED_IPSauf vertrauenswürdige IPs beschränkenADMIN_ALLOWED_IPSauf Office/VPN-IPs setzen- Redis für verteiltes Rate Limiting konfigurieren
- CSRF_SECRET in .env setzen (min. 32 Zeichen)
- 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
_payloadJSON-Feld (Payload Admin Panel Format) - Standard FormData (
multipart/form-data) - URL-encoded (
application/x-www-form-urlencoded)
- JSON (
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 |
|---|---|
| 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 |