Modern Web Mimarilerinde Server Actions SSRF ve Zafiyet Analizi
1. Zafiyetin Ortaya Çıkışı ve Tehdit Modeli
React Server Components (RSC) ve Server Actions mimarisinde istemciden gelen POST istekleri, Next.js dahili yönlendiricisi tarafından `_rsc` veya özel `Next-Action` başlıkları aracılığıyla ayrıştırılır. Uygulama bir ters proxy (Reverse Proxy) arkasında çalıştığında ve `Host` veya `X-Forwarded-Host` başlığı sıkı bir whitelist filtresinden geçirilmediğinde, sunucu tarafında oluşturulan internal fetch çağrıları veya callback mekanizmaları saldırgan tarafından kontrol edilen harici adreslere ya da dahili link-local cloud metadata servislerine (örn. 169.254.169.254) yönlendirilebilir.
2. PoC: Güvensiz Server Action ve Host Zehirleme
Aşağıdaki zafiyetli kod bloğunda, sunucu tarafında çalışan Server Action, istemciden gelen isteğin Host başlığını doğrudan dahili mikroservis isteğinde kullanmaktadır. Bu durum saldırganın internal ağdaki IAM kasasını veya Redis önbelleğini okumasına olanak tanır:
// [!] VULNERABLE PATTERN: Güvensiz Host başlığı ve denetimsiz redirect
export async function triggerInternalSync(callbackPath: string) {
'use server';
const headersList = await headers();
const rawHost = headersList.get('host') || 'localhost:3000';
// Saldırgan 'Host: 169.254.169.254' veya 'evil-listener.com' yolladığında SSRF gerçekleşir!
const targetUrl = `http://${rawHost}${callbackPath}`;
const res = await fetch(targetUrl, {
headers: { 'X-Internal-Secret': process.env.INTERNAL_SERVICE_KEY! }
});
return await res.json();
}3. Savunma ve Güvenli Kodlama (Remediation)
Zafiyeti engellemek için üç katmanlı savunma stratejisi uygulanmalıdır: Birincisi, `allowedOrigins` direktifi ile `next.config.ts` seviyesinde Server Action origin kısıtlaması; ikincisi, Zod ile URL şemasının ve host adının doğrulanması; üçüncüsü ise dahili IP aralıklarına (RFC 1918 ve RFC 3927) giden isteklerin ağ seviyesinde bloke edilmesidir.
// [*] SECURE PATTERN: Sıkı Host doğrulaması ve RFC 1918 izolasyonu
import { z } from 'zod';
const ALLOWED_INTERNAL_HOSTS = new Set([
'api.internal.ulusiber.org',
'services.internal.ulusiber.org'
]);
export async function triggerSecureSync(path: string) {
'use server';
// 1. Zod ile girdi doğrulama
const cleanPath = z.string().regex(/^\/[a-zA-Z0-9_-]+$/).parse(path);
// 2. Host başlığını asla istemciden almayın! Sabit yapılandırma kullanın:
const verifiedHost = process.env.INTERNAL_SERVICE_HOST;
if (!verifiedHost || !ALLOWED_INTERNAL_HOSTS.has(verifiedHost)) {
throw new Error("Geçersiz veya yetkisiz mikroservis hedefi.");
}
return await fetch(`https://${verifiedHost}${cleanPath}`);
}Önemli Çıkarımlar ve Savunma Tavsiyeleri
- [1]Server Action isteklerinde `Host` veya `X-Forwarded-Host` başlıklarına asla güvenmeyin.
- [2]Tüm dahili yönlendirmeleri katı whitelist mekanizmalarına bağlayın.
- [3]Cloud metadata adreslerine (169.254.169.254) erişimi iptables / AWS IMDSv2 ile kısıtlayın.