From 6af1a4bbd4cb82ad083bddc6cd480ceda5b415b1 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sat, 16 May 2026 18:24:21 +0200 Subject: [PATCH] =?UTF-8?q?fix(security):=20trust=20proxy=20=3D=201=20bei?= =?UTF-8?q?=20HTTPS=5FENABLED=20=E2=80=93=20echte=20Client-IP=20statt=20Pr?= =?UTF-8?q?oxy-IP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wenn der TLS-Reverse-Proxy (Nginx Proxy Manager) auf einer SEPARATEN Box läuft, kommt nicht von 127.0.0.1 → `trust proxy = 'loopback'` greift nicht → req.ip bleibt die NPM-IP statt der echten Client-IP. Folgen: - Rate-Limiter sieht alle Angriffe als von "einem" Client (= NPM) - Security-Monitor loggt Proxy-IP statt Angreifer-IP (Beweis im Audit-Log: "ACCESS_DENIED ... 172.0.2.12" für alle Versuche) - IDOR-Threshold-Detection (>5 in 5 min pro IP) triggert auf der NPM-IP und blockt damit alle legitimen User durch denselben Proxy Fix: bei HTTPS_ENABLED=true `trust proxy = 1` (vertraue genau einem Hop – den vorgelagerten TLS-Proxy). Bei HTTPS_ENABLED=false bleibt es bei `loopback` (keine Proxy-Annahme bei direkter http://ip:port-Nutzung). Voraussetzung für HTTPS_ENABLED=true: Backend ist nicht direkt aus dem Internet erreichbar, sonst könnte ein direkter Connect ein X-Forwarded-For faken und den Limiter umgehen. Bei NPM-Setup gewährleistet durch Docker-Network + nicht-veröffentlichten Backend-Port. Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/src/index.ts | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index fa0c64ae..363f49ae 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -84,16 +84,25 @@ if (!process.env.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY.length !== 64) { const app = express(); const PORT = process.env.PORT || 3001; -// Hinter einem Reverse-Proxy (Nginx/Plesk) läuft der Server typisch auf localhost. -// `trust proxy = 'loopback'` vertraut nur Connections von 127.0.0.1 / ::1 -// (= lokaler Reverse-Proxy). Damit kann ein Angreifer mit DIREKTEM Zugriff -// auf das Backend nicht via X-Forwarded-For den Rate-Limiter umgehen, -// während gleichzeitig der lokale Reverse-Proxy die echte Client-IP liefern darf. +// Trust-Proxy-Konfiguration für `req.ip` und `X-Forwarded-For`. // -// WICHTIG für Production: Backend nur auf 127.0.0.1 lauschen lassen -// (LISTEN_ADDR=127.0.0.1) – sonst kann ein direkter Connect von außen -// trotzdem als loopback gelten, falls das Routing das so durchstellt. -app.set('trust proxy', 'loopback'); +// Zwei Szenarien: +// 1) **HTTPS_ENABLED=true** (Produktion mit vorgelagertem TLS-Proxy auf +// EIGENER Box, z.B. Nginx Proxy Manager): `trust proxy = 1` vertraut +// genau einem Hop → req.ip = echter Client (nicht der Proxy). +// Voraussetzung: Backend ist NICHT direkt aus dem Internet erreichbar, +// sonst könnte ein Direkt-Connect X-Forwarded-For faken und den +// Rate-Limiter / Security-Monitor umgehen. Bei NPM-Setup ist das +// durch das Docker-Network + nicht-veröffentlichten Backend-Port +// gewährleistet. +// 2) **HTTPS_ENABLED=false** (lokales Dev oder direkter http://ip:port- +// Zugriff): `loopback` reicht – kein vertrauenswürdiger Hop davor. +// +// Vor dem Fix stand das auf `'loopback'` was im Produktiv-NPM-Setup +// IMMER die Proxy-IP statt der Client-IP lieferte → Rate-Limit und +// IDOR-Threshold-Detection sahen alle Angriffe als von „einem" Client. +const trustProxyValue = process.env.HTTPS_ENABLED === 'true' ? 1 : 'loopback'; +app.set('trust proxy', trustProxyValue); // ==================== SECURITY MIDDLEWARE ====================