Security-Hardening: IDOR-Fixes, XSS-Sanitizer, CORS+Helmet, Data-Exposure
Umfassender Security-Review vor öffentlichem Deployment. Detaillierter Report in docs/SECURITY-REVIEW.md. 🔴 KRITISCHE FIXES: 1. CORS offen → jetzt nur explizite Origins (via CORS_ORIGINS env), in Production per default komplett aus (gleiche Origin erzwingt Browser). 2. Keine Security-Headers → helmet-Middleware hinzugefügt. X-Frame-Options, X-Content-Type-Options, HSTS, Referrer-Policy, CORP. 3. JWT-Fallback-Secret entfernt. Beim Server-Start wird jetzt geprüft ob JWT_SECRET (min 32 Zeichen) und ENCRYPTION_KEY (exakt 64 Hex) gesetzt sind, sonst Fail-Fast mit klarer Fehlermeldung. 4. IDOR bei 7 Contract-Endpoints. Portal-Kunden mit 'contracts:read' konnten über geratene IDs fremde Daten abrufen (Passwort, SIM-PIN/PUK, Internet-Zugangsdaten, SIP-Credentials, Vertragsdokumente, Rechnungen). Neuer Helper canAccessContract() in utils/accessControl.ts in allen betroffenen Endpoints eingebaut. Prüft Vertrag-Besitzer + Vollmachten. 5. XSS via Email-Body. email.htmlBody wurde ungefiltert via dangerouslySetInnerHTML gerendert. Angreifer konnte Mail mit <script> schicken → Token-Diebstahl aus localStorage. Jetzt mit DOMPurify sanitized: verbietet script/iframe/form/inline-handler, erlaubt normale Formatierung + Bilder. 6. Customer-API leakte sensible Felder: - portalPasswordHash (bcrypt-Hash) - portalPasswordEncrypted (symmetrisch, mit ENCRYPTION_KEY entschlüsselbar) - portalPasswordResetToken (gültig 2h) Neuer Sanitizer in utils/sanitize.ts, angewendet in getCustomer/getCustomers. Admin mit customers:update darf portalPasswordEncrypted sehen (für UI-Anzeige), alle anderen Rollen nicht. 🟡 WICHTIGE FIXES: 7. Portal-JWT-Invalidation nach Passwort-Reset. Neues Feld Customer.portalTokenInvalidatedAt, wird beim Reset auf now() gesetzt. Auth-Middleware prüft Portal-Sessions dagegen. Alte Sessions werden dadurch invalidiert. 8. express.json() mit 5 MB Size-Limit (statt Default 100 KB unklar). Neue Files: - backend/src/utils/accessControl.ts - IDOR-Schutz - backend/src/utils/sanitize.ts - Response-Sanitizer - docs/SECURITY-REVIEW.md - vollständiger Report + Deployment-Checkliste Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+41
-3
@@ -1,5 +1,6 @@
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import path from 'path';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
@@ -39,12 +40,49 @@ import { auditMiddleware } from './middleware/audit.js';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
// ==================== SECURITY: Pflicht-Umgebungsvariablen prüfen ====================
|
||||
if (!process.env.JWT_SECRET || process.env.JWT_SECRET.length < 32) {
|
||||
console.error('❌ JWT_SECRET ist nicht gesetzt oder zu kurz (min. 32 Zeichen)');
|
||||
console.error(' Generiere mit: openssl rand -hex 64');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!process.env.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY.length !== 64) {
|
||||
console.error('❌ ENCRYPTION_KEY ist nicht gesetzt oder hat nicht exakt 64 Hex-Zeichen (32 Byte)');
|
||||
console.error(' Generiere mit: openssl rand -hex 32');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
// ==================== SECURITY MIDDLEWARE ====================
|
||||
|
||||
// HTTP Security Headers (X-Frame-Options, X-Content-Type-Options, HSTS, etc.)
|
||||
app.use(
|
||||
helmet({
|
||||
// CSP ausschalten – wird bei SPA schwierig, frontend setzt eigene CSP via meta
|
||||
contentSecurityPolicy: false,
|
||||
// Cross-Origin-Resource-Policy: "same-site" für SPA mit gleicher Origin
|
||||
crossOriginResourcePolicy: { policy: 'same-site' },
|
||||
}),
|
||||
);
|
||||
|
||||
// CORS: in Production nur explizit erlaubte Origins. In Dev: alles erlauben.
|
||||
const corsOrigins = process.env.CORS_ORIGINS
|
||||
? process.env.CORS_ORIGINS.split(',').map((s) => s.trim())
|
||||
: process.env.NODE_ENV === 'production'
|
||||
? false // Gar kein Cross-Origin zulässig (Frontend wird unter gleicher Origin ausgeliefert)
|
||||
: true; // Dev: alles erlauben
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
origin: corsOrigins,
|
||||
credentials: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// JSON-Body-Limit: 5 MB (Uploads laufen über multer, brauchen kein json())
|
||||
app.use(express.json({ limit: '5mb' }));
|
||||
|
||||
// Audit-Logging Middleware (DSGVO-konform)
|
||||
app.use(auditContextMiddleware);
|
||||
|
||||
Reference in New Issue
Block a user