Files
Artista-Filmcamp/tests/client-ip.test.ts
T
Nick SzaunigandClaude Opus 5.5 f75a5f7769
Docker-Images bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
fix(security): Client-IP nicht aus X-Real-IP, better-auth nur mit Server-Wert
Traefik vertraut seit 2026-10-12 den weitergeleiteten Headern aus
Cloudflare-Netzen. Wer eine eigene Cloudflare-Zone auf den Server zeigen
laesst, konnte damit X-Real-IP und den ersten X-Forwarded-For-Eintrag
faelschen und die Rate-Limits (Buchung, Kontakt, Login, 2FA) umgehen.

- clientIp: Gegenstelle ist der letzte X-Forwarded-For-Eintrag (von Traefik
  angehaengt); CF-Connecting-IP nur, wenn sie eine Cloudflare-Edge ist.
- better-auth liest nur noch x-artista-client-ip, den der Auth-Endpunkt
  per withClientIp setzt (ein mitgeschickter Wert wird ersetzt).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-12 09:49:58 +02:00

32 lines
1.6 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { CLIENT_IP_HEADER, clientIp, withClientIp } from "@/lib/rate-limit";
const h = (values: Record<string, string>) => new Headers(values);
describe("clientIp hinter Traefik", () => {
test("Gegenstelle ist der letzte Eintrag in X-Forwarded-For, nie ein Eintrag des Clients", () => {
expect(clientIp(h({ "x-forwarded-for": "1.2.3.4, 198.51.100.9", "x-real-ip": "1.2.3.4" }))).toBe("198.51.100.9");
});
test("über Cloudflare gefälschtes X-Real-IP zählt nicht, CF-Connecting-IP nur von Cloudflare-Edges", () => {
const viaCloudflare = { "x-real-ip": "1.2.3.4", "x-forwarded-for": "1.2.3.4, 203.0.113.7, 172.70.1.2", "cf-connecting-ip": "203.0.113.7" };
expect(clientIp(h(viaCloudflare))).toBe("203.0.113.7");
expect(clientIp(h({ "x-forwarded-for": "198.51.100.9", "cf-connecting-ip": "203.0.113.7" }))).toBe("198.51.100.9");
});
test("ohne X-Forwarded-For (lokal) gilt X-Real-IP, ungültige Werte ergeben „unknown“", () => {
expect(clientIp(h({ "x-real-ip": "198.51.100.9" }))).toBe("198.51.100.9");
expect(clientIp(h({}))).toBe("unknown");
expect(clientIp(h({ "x-forwarded-for": "1.2.3.4, kein-ip" }))).toBe("unknown");
});
test("withClientIp setzt den Header für better-auth und überschreibt einen mitgeschickten Wert", () => {
const request = new Request("https://example.org/api/auth/sign-in/email", {
method: "POST",
headers: { "x-forwarded-for": "1.2.3.4, 198.51.100.9", [CLIENT_IP_HEADER]: "1.2.3.4" },
body: "{}",
});
expect(withClientIp(request).headers.get(CLIENT_IP_HEADER)).toBe("198.51.100.9");
});
});