React2Shell: React Server Components Üzerinden RCE Açığı
ibrahim- 24 Ara 2025

React Server Components Üzerinden RCE Açığını İnceliyoruz
Son dönemde “React hacklendi” başlıklarıyla gündeme gelen React2Shell açığı, aslında React’in kendisinden çok, React Server Components (RSC) mimarisinin yanlış ve kontrolsüz kullanımından kaynaklanan kritik bir Remote Code Execution (RCE) problemidir. Sektörde bu isimle popülerleşse de, teknik literatürde bu durum "RSC Payload Injection" veya "Prototype Pollution leading to RCE" olarak geçer.
🚨 RCE (Remote Code Execution) Neden En Tehlikeli Açıklardan Biri?
Bir RCE açığı şunu ifade eder:
Saldırgan, uygulamaya gönderdiği bir istekle, sunucu üzerinde kendi istediği kodu çalıştırabilir.
Bu noktadan sonra kontrol tamamen saldırgana geçer. Uygulamanızın tüm güvenlik mekanizmaları anlamsızlaşır:
- Yetkilendirme anlamsızlaşır: Admin paneline şifreyle girmek yerine, veritabanına doğrudan arka kapıdan erişilir.
- Veri Sızıntısı: Veritabanı, dosya sistemi ve gizli
.envdeğişkenlerindeki (API anahtarları, hassas konfigürasyonlar) bilgiler saniyeler içinde çalınabilir. - Pivot Saldırılar: Sunucunuz, saldırganın iç ağdaki diğer sistemlere saldırması için bir "sıçrama tahtası" (pivot point) olarak kullanılır.
React2Shell tam olarak bu sınıfa giren, hafife alınmaması gereken bir güvenlik tehdididir.
🏗️ React Server Components (RSC) Mimarisi ve "Büyük Güç, Büyük Sorumluluk"
React Server Components, bileşenlerin Node.js ortamında render edilmesini sağlayarak bize müthiş bir performans ve geliştirme deneyimi sunar.
Örnek bir RSC:
// app/page.tsx
export default async function Page() {
const data = await fetchDataFromDB() // Doğrudan sunucu yetkisiyle veritabanı erişimi!
return <div>{data}</div>
}
Bu bileşenin çalıştığı ortamın temel özellikleri:
-
Tarayıcıda çalışmaz: Client-side JavaScript bundle'ına dahil edilmez.
-
Node.js üzerinde render edilir: Doğrudan sunucu yetkilerine ve işletim sistemi kaynaklarına erişebilir.
Unutmayın: Bu bileşenin çalıştığı ortam, doğrudan sunucu yetkilerine sahiptir. Bu da demektir ki, gücünüz arttıkça güvenlik sorumluluğunuz da katlanarak artar.
🧪 Açığın Kalbi: Serialization, Deserialization ve Prototype Pollution
RSC mimarisinde istemci ile sunucu arasında özel bir veri protokolü kullanılır. Bu akışta; bileşen referansları, props ve state snapshot'ları gibi veriler serialize (serileştirme) edilerek taşınır. Açığın sinsi noktası işte tam da bu noktada ortaya çıkar:
-
Girdi Manipülasyonu: Saldırgan, normal bir string veya ID yerine, JavaScript'in iç yapısını hedef alan
__proto__veya constructor gibi özel property'lere sahip, manipüle edilmiş objeler gönderir. Bu, genellikle URL parametreleri veya HTTP header'ları aracılığıyla gerçekleştirilir. -
Prototype Pollution: Bu manipüle edilmiş objeler sunucu tarafında işlenirken (deserialization veya hydration sırasında), Node.js ortamındaki global obje prototipleri kirlenir. Örneğin, tüm objelerin toString metodunu değiştirebilir.
-
Payload Aktivasyonu: React, bu "kirli" objeyi bir bileşen proprosu olarak işlediğinde veya bir Server Action'da kullanıldığında, saldırganın enjekte ettiği executable context (çalıştırılabilir kod) sunucu tarafında (Node.js) yürütülür.
Basit bir Serialization/Deserialization Problemi Örneği:
const payload = JSON.parse(clientInput) // clientInput kontrol edilmeden parse edilirse
process(payload) // Ve payload içinde çalıştırılabilir bir yapı varsa...
Eğer payload içeriği yeterince doğrulanmazsa, beklenmeyen tipler engellenmezse veya fonksiyon/executable context filtrelenmezse, deserialize edilen veri doğrudan çalıştırılabilir bir forma dönüşebilir.
⚔️ Temsili Bir Saldırı Akışı
⚠️ Eğitim amaçlı basitleştirilmiş bir örnek olup, gerçek saldırılar çok daha karmaşık olabilir.
Saldırgan, uygulamanın RSC endpoint'ini (POST /_rsc gibi) veya bir Server Action'ı hedef alır.
Gönderilen veri normal bir React payload’ı gibi görünür, ancak içinde Node.js runtime'ını manipüle edecek bir executable context barındırır. Amaç; process, require veya child_process gibi tehlikeli Node.js primitive'lerine ulaşmaktır.
Başarılı olduğunda saldırgan şunları test eder:
// Sunucu ortam değişkenlerini görüntüleme
process.env
// Sunucudaki kök dizini listeleme denemesi
require("fs").readdirSync("/")
Bu noktada kontrol tamamen sunucu tarafına geçer.
🧪 Uygulamanızı Test Edin: Basit Bir PoC (Proof of Concept)
Bir sistemin bu tür bir manipülasyona karşı ne kadar dirençli olduğunu anlamanın en iyi yolu, ona "saldırgan gibi" bir veri göndermektir. Aşağıdaki Node.js script'i, bir RSC endpoint'ine sinsi bir __proto__ payload'ı göndererek sunucunun bu veriyi nasıl karşıladığını test eder.
const axios = require('axios');
/**
* React2Shell (RSC Injection) Test Script
* Bu script, sunucunun Prototype Pollution saldırılarına
* karşı savunmasız olup olmadığını test etmek için kullanılır.
*/
async function checkVulnerability() {
const TARGET_URL = 'http://localhost:3000/_rsc'; // Kendi test ortamınız
console.log(`[*] Hedef test ediliyor: ${TARGET_URL}`);
// Saldırganın sunucu tarafındaki global objeleri manipüle
// etmek için gönderebileceği örnek payload.
const maliciousPayload = {
"__proto__": {
"vulnerable": "true",
"isAdmin": true
},
"id": "test-123"
};
try {
const response = await axios.post(TARGET_URL, maliciousPayload, {
headers: {
'Content-Type': 'text/x-component',
'Next-Router-State-Tree': '1'
}
});
console.log(`[+] İstek durumu: ${response.status}`);
console.log("[!] Eğer sunucu bu veriyi hata vermeden (Zod vb. takılmadan) kabul ediyorsa,");
console.log(" input validation mekanizmanızı gözden geçirmelisiniz.");
} catch (error) {
console.log("[-] İstek başarısız veya engellendi. (Bu iyiye işaret olabilir!)");
}
}
checkVulnerability();
⚠️ Güvenlik Notu: Bu script sadece kendi yerel ortamınızda veya izin aldığınız sistemlerde test yapmanız için paylaşılmıştır. Yetkisiz sistemler üzerinde bu tür testler yapmak etik dışıdır ve yasal sonuçlar doğurabilir.
Bu Script Bize Ne Gösterir?
Eğer sunucunuz bu isteği hiçbir doğrulama (Validation) hatası fırlatmadan kabul ediyorsa, gönderdiğiniz __proto__ objesi sunucu belleğinde bir yerlere sızmış olabilir. Bu, saldırganın bir sonraki adımda sistem fonksiyonlarını (process, require) manipüle ederek RCE tetiklemesi için gereken zemini hazırlar.
🎯 Bu Açık Neden Herkesi Etkilemiyor?
React2Shell, her React uygulamasını etkilemez, çünkü:
-
RSC kullanmayan projeler etkilenmez: Client-only React uygulamaları veya SSR kullanmayan SPA'lar doğrudan hedef değildir.
-
Client-only React uygulamaları etkilenmez: Sunucu tarafında kod çalıştırmadıkları için risk taşımazlar.
-
Güncel ve doğru yapılandırılmış Next.js projelerinde risk düşüktür: Ancak bu "düşük" riskin sıfır olmadığını ve geliştiricinin rolünü göz ardı etmemek gerekir.
Unutulmamalıdır ki: React2Shell, yanlış yapılandırılmış server-side React mantığını hedef alır.
❌ Hatalı Kullanıma Teknik Bir Örnek
Güvenlik mekanizmalarından yoksun bir Server Action:
// ❌ Güvensiz örnek
export async function Action(data: any) {
// Gelen veriye güveniliyor ve doğrudan işleniyor
return processData(data)
}
Burada:
-
any kullanımıyla tip güvenliği bypass edilmiş.
-
Herhangi bir doğrulama (validation) mekanizması yok.
-
Beklenmeyen bir veri şemasına karşı koruma yok.
Bu, RCE için doğrudan bir davetiye çıkarır.
✅ Daha Güvenli Bir Yaklaşım: Zod ile Katı Input Validation
Gelen veriyi her zaman, her koşulda doğrulamak esastır. zod gibi schema validation kütüphaneleri bu konuda hayat kurtarıcıdır.
import { z } from "zod"
// Sadece 'id' adında bir string kabul eden katı bir şema
const schema = z.object({
id: z.string().uuid(), // id'nin bir UUID formatında olmasını zorunlu kılar
}).strict(); // <-- ÖNEMLİ: Şemada belirtilmeyen fazladan hiçbir property'yi kabul etmez!
export async function Action(input: unknown) {
// Gelen veriyi şemaya göre parse etmeye çalışır.
// Doğrulama başarısız olursa burada hata fırlatır ve işlem durur.
const data = schema.parse(input);
return safeProcess(data); // Güvenli bir şekilde işleme devam et
}
Burada:
-
Tip güvenliği: unknown tipi ve z.string().uuid() ile güçlü tip kontrolü sağlanır.
-
Validation: Gelen verinin beklenen formatta olup olmadığı doğrulanır.
-
Beklenmeyen yapıların engellenmesi: .strict() metodu, saldırganın
__proto__gibi ekstra property'ler eklemesini doğrudan engeller.
💡 React2Shell Bize Ne Öğretti?
"Artık React geliştiricisi olmak, backend güvenliğini bilmeden mümkün değil."
Bu açık, React’in bir zayıflığı değil, server-side JavaScript’in gücünün ve beraberindeki tehlikelerin bir göstergesidir. Modern frontend framework’lerinin artık tam anlamıyla bir backend sorumluluğu taşıdığını çok net bir şekilde gösterdi.
Artık React projeleri:
-
Sadece UI değil, aynı zamanda sunucu tarafında kritik iş mantığı çalıştırıyor.
-
Bu yüzden geliştiriciler, yalnızca UI/UX değil, aynı zamanda sunucu güvenliği prensiplerini de derinlemesine anlamalıdır.
Eğer React Server Components kullanıyorsan, artık sadece bir component değil, aynı zamanda bir attack surface (saldırı yüzeyi) de yazdığını bilmelisin.
🛡️ Savunma Stratejileri (Özet ve Eylem Planı)
Uygulamanızı React2Shell ve benzeri RCE açıklarına karşı korumak için izlemeniz gereken adımlar:
-
React / Next.js Sürümünüzü Güncel Tutun: Özellikle Next.js v14.1.1 ve üzeri sürümler, bu tür injection saldırılarına karşı önemli yamalar içerir.
-
RSC Kullanımını Minimal Tutun: Gerekmeyen her yerde Server Components veya Server Actions kullanmaktan kaçının. Her use server direktifi yeni bir potansiyel güvenlik kapısı demektir.
-
Tüm Inputlara Zorunlu Validation Uygulayın: Kullanıcıdan gelen her türlü veriyi (URL parametreleri, HTTP başlıkları, form girdileri) zod gibi kütüphanelerle katı bir şekilde doğrulayın ve .strict() metodunu aktif olarak kullanın.
-
Ortam Değişkenlerini Asla Client'a Yaklaştırmayın: Hassas .env değişkenlerinin NEXT_PUBLIC_ ön eki almadığından emin olun.
-
Güvenli Serialization Kütüphaneleri Kullanın: Sunucudan istemciye veri aktarırken, serialize-javascript gibi karakter kaçışlarını (escaping) otomatik yapan güvenli kütüphaneleri tercih edin. Asla çıplak JSON.stringify kullanmayın.
-
Monitoring ve Loglama Ekleyin: Üretim ortamında Sentry gibi araçlarla anormal POST /_rsc isteklerini veya Server Action hatalarını sürekli izleyin.
Sonuç
React2Shell:
-
Abartıldığı gibi “React’in sonu” değil.
-
Ama hafife alınacak, göz ardı edilecek bir açık da değil.
Bu olay, modern web geliştirme paradigmasının artık tam anlamıyla backend güvenlik sorumluluğu taşıdığını çok net bir şekilde gösterdi. Yeni nesil framework'lerin gücüyle birlikte gelen bu sorumluluğu kavramak, daha güvenli ve sağlam web uygulamaları inşa etmenin anahtarıdır.
🤝 İletişimde Kalalım
React2Shell ve web güvenliği hakkındaki görüşleriniz benim için çok değerli. Merak ettiklerinizi sormak veya deneyimlerinizi paylaşmak için bana aşağıdaki kanallardan doğrudan ulaşabilirsiniz.
Sizden haber bekliyorum!
