fix(security): make trustProxy safe so per-IP throttle can't be XFF-spoofed (RT-6.2) #61

Closed
opened 2026-06-21 01:56:54 +03:00 by Ghost · 0 comments

Источник: red-team-аудит, RT-6.2 (docs/red-team-report.md).
Модель угроз: анонимный ассистент снаружи; per-IP throttle — первая стена против флуда.

Что происходит

apps/server/src/main.ts:22FastifyAdapter({ trustProxy: true }), поэтому req.ip берётся из X-Forwarded-For, который клиент подделывает. per-IP throttle 5/min на POST /api/shares/ai/stream (public-share-chat.controller.ts:71) обходится ротацией заголовка. Тот же req.ip влияет на brute-лимитер /mcp и логи.

Фикс

Раз сервис выставляется наружу — он всё равно за reverse-proxy. Достаточно настроить прокси так, чтобы он перезаписывал (а не дописывал) X-Forwarded-For реальным IP клиента (см. .env.example:5-13). Тогда per-IP throttle оживает без правок кода.

Опционально (defense-in-depth) — сделать trustProxy env-настраиваемым с безопасным дефолтом:

// main.ts — trust ONLY real proxies on private/loopback nets by default, so a
// public-IP client can never spoof its IP via X-Forwarded-For.
function resolveTrustProxy(raw?: string): boolean | number | string {
  if (raw == null || raw === '') return 'loopback, linklocal, uniquelocal';
  if (raw === 'true') return true;
  if (raw === 'false') return false;
  const n = Number(raw);
  return Number.isInteger(n) && n >= 0 ? n : raw; // hop count or CIDR/IP list
}
// new FastifyAdapter({ trustProxy: resolveTrustProxy(process.env.TRUST_PROXY) })

Цена / приоритет

В основном конфиг прокси (код не обязателен). Глобальная правка — нужна регресс-проверка, что за прокси req.ip = реальный клиентский IP.

**Источник:** red-team-аудит, RT-6.2 (`docs/red-team-report.md`). **Модель угроз:** анонимный ассистент снаружи; per-IP throttle — первая стена против флуда. ### Что происходит `apps/server/src/main.ts:22` — `FastifyAdapter({ trustProxy: true })`, поэтому `req.ip` берётся из `X-Forwarded-For`, который клиент подделывает. per-IP throttle `5/min` на `POST /api/shares/ai/stream` (`public-share-chat.controller.ts:71`) обходится ротацией заголовка. Тот же `req.ip` влияет на brute-лимитер `/mcp` и логи. ### Фикс Раз сервис выставляется наружу — он всё равно за reverse-proxy. Достаточно настроить прокси так, чтобы он **перезаписывал** (а не дописывал) `X-Forwarded-For` реальным IP клиента (см. `.env.example:5-13`). Тогда per-IP throttle оживает без правок кода. Опционально (defense-in-depth) — сделать `trustProxy` env-настраиваемым с безопасным дефолтом: ```ts // main.ts — trust ONLY real proxies on private/loopback nets by default, so a // public-IP client can never spoof its IP via X-Forwarded-For. function resolveTrustProxy(raw?: string): boolean | number | string { if (raw == null || raw === '') return 'loopback, linklocal, uniquelocal'; if (raw === 'true') return true; if (raw === 'false') return false; const n = Number(raw); return Number.isInteger(n) && n >= 0 ? n : raw; // hop count or CIDR/IP list } // new FastifyAdapter({ trustProxy: resolveTrustProxy(process.env.TRUST_PROXY) }) ``` ### Цена / приоритет В основном **конфиг прокси** (код не обязателен). Глобальная правка — нужна регресс-проверка, что за прокси `req.ip` = реальный клиентский IP.
Ghost added the bugsecurity labels 2026-06-21 02:27:19 +03:00
Ghost closed this issue 2026-06-21 14:10:28 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#61