WEB-SECURITY BROWSER-SECURITY SAME-ORIGIN-POLICY CROSS-ORIGIN-COMMUNICATION JAVASCRIPT WEB-API SECURITY-BEST-PRACTICES LEGACY-CODE FRONTEND-SECURITY DEPRECATION MODERN-WEB

Masa Lalu dan Masa Depan Komunikasi Cross-Origin: Menggali `document.domain` dan Alternatif Modern yang Aman

⏱️ 6 menit baca
👨‍💻

Masa Lalu dan Masa Depan Komunikasi Cross-Origin: Menggali document.domain dan Alternatif Modern yang Aman

Dalam dunia pengembangan web, komunikasi antara berbagai bagian aplikasi—terutama yang berjalan di domain berbeda atau subdomain—adalah hal yang umum. Namun, hal ini juga menjadi salah satu area paling rawan kerentanan keamanan. Dulu, ada satu properti JavaScript yang sering digunakan untuk memfasilitasi komunikasi ini: document.domain.

Jika Anda pernah bekerja dengan aplikasi web lama atau codebase yang kompleks, kemungkinan besar Anda pernah menemukan atau bahkan menggunakan document.domain. Properti ini memungkinkan developer untuk “merelaksasi” kebijakan keamanan browser yang fundamental, yaitu Same-Origin Policy (SOP). Tapi, seperti banyak alat lama di web, apa yang dulunya praktis kini menjadi sumber masalah keamanan dan sudah usang.

Artikel ini akan membawa Anda menyelami sejarah document.domain, mengapa ia menjadi berbahaya, dan yang terpenting, bagaimana kita bisa beralih ke alternatif modern yang lebih aman dan efisien. Jika aplikasi Anda masih mengandalkan document.domain, atau Anda sekadar ingin memahami lebih dalam tentang keamanan komunikasi cross-origin, Anda berada di tempat yang tepat!

1. Sekilas tentang Same-Origin Policy (SOP)

Sebelum kita menyelami document.domain, mari kita pahami dulu fondasi keamanannya: Same-Origin Policy (SOP). 📌 SOP adalah fitur keamanan penting di browser yang membatasi bagaimana dokumen atau skrip yang dimuat dari satu “origin” dapat berinteraksi dengan sumber daya dari “origin” lain.

Origin didefinisikan oleh kombinasi protokol (misalnya http, https), host (misalnya www.example.com), dan port (misalnya 80, 443). Jika salah satu dari ketiga elemen ini berbeda, maka dua sumber daya dianggap berasal dari origin yang berbeda.

Tujuan utama SOP adalah mencegah skrip berbahaya di satu situs web mengakses data sensitif di situs web lain. Bayangkan jika sebuah situs web jahat bisa membaca data dari akun bank Anda yang sedang terbuka di tab lain; itu adalah mimpi buruk keamanan yang dicegah oleh SOP.

SOP memengaruhi banyak operasi, termasuk:

2. document.domain: Sejarah, Cara Kerja, dan Masalahnya

Dulu, SOP seringkali terasa terlalu ketat, terutama untuk aplikasi yang kompleks yang menggunakan banyak subdomain, seperti app.example.com dan api.example.com. Developer ingin agar app.example.com bisa berkomunikasi langsung dengan api.example.com tanpa batasan SOP, karena keduanya secara logis adalah bagian dari aplikasi yang sama.

Di sinilah document.domain berperan. Properti ini memungkinkan skrip untuk mengatur domain dari dokumen saat ini ke domain induk yang lebih umum. Misalnya, jika app.example.com dan api.example.com sama-sama mengatur document.domain = "example.com", maka browser akan memperlakukan keduanya seolah-olah berasal dari origin yang sama, yaitu example.com. Ini secara efektif “merelaksasi” SOP.

Contoh Cara Kerja document.domain (Era Lama):

// Di app.example.com
document.domain = "example.com";

// Di iframe yang dimuat dari api.example.com
// Setelah iframe juga mengatur document.domain = "example.com"
// Maka app.example.com dan iframe bisa saling mengakses DOM
// atau memanggil fungsi JavaScript secara langsung.

Masalah document.domain: Gerbang Kerentanan

Meski terdengar praktis, document.domain memiliki cacat fundamental yang serius:

  1. Global State dan Privilege Escalation: Ketika Anda mengatur document.domain, Anda memengaruhi semua frame dan window lain yang juga mengatur domain yang sama. Ini berarti jika ada satu subdomain (misalnya malicious.example.com) yang berhasil disusupi dan mengatur document.domain = "example.com", ia bisa mendapatkan akses ke dokumen dari semua subdomain lain yang juga telah mengatur document.domain = "example.com", termasuk app.example.com atau bank.example.com (jika ada). Ini adalah bentuk privilege escalation yang berbahaya.
  2. Mengurangi Isolasi: Tujuan SOP adalah isolasi. document.domain secara langsung mengikis isolasi ini, membuka celah bagi serangan Cross-Site Scripting (XSS) atau eksfiltrasi data jika salah satu bagian dari domain induk disusupi.
  3. Deprekasi dan Pembatasan Modern: Browser modern semakin membatasi atau bahkan mendeprekasi penggunaan document.domain. Sejak Chrome 109, Firefox 119, dan Safari 16.4, pengaturan document.domain telah dinonaktifkan secara default di dalam <iframe> yang di-embed dari origin yang berbeda. Ini berarti jika frame induk dan frame child berada di subdomain yang berbeda, mereka tidak lagi dapat menggunakan document.domain untuk berkomunikasi. Jika Anda mencoba mengaturnya, akan ada pesan error di konsol.

⚠️ Peringatan: Penggunaan document.domain sangat tidak dianjurkan untuk aplikasi web modern. Jika Anda masih menggunakannya, segera cari alternatif.

3. Mengapa document.domain Berbahaya? Studi Kasus Sederhana

Bayangkan sebuah skenario: Anda memiliki aplikasi e-commerce (shop.example.com) yang mengandalkan document.domain = "example.com" untuk berkomunikasi dengan payment.example.com (juga mengatur document.domain = "example.com").

Di sisi lain, Anda juga memiliki situs blog lama (blog.example.com) yang kurang terawat, dan secara tidak sengaja mengizinkan User-Generated Content (UGC) tanpa sanitasi yang memadai. Seorang penyerang berhasil menyuntikkan skrip XSS ke blog.example.com.

Skrip XSS di blog.example.com bisa melakukan hal berikut:

  1. Mengatur document.domain = "example.com".
  2. Mencari <iframe> dari shop.example.com atau payment.example.com yang juga telah mengatur document.domain = "example.com".
  3. Setelah domain disesuaikan, skrip jahat dari blog.example.com kini memiliki akses penuh ke DOM dan JavaScript di <iframe> yang rentan. Ini berarti penyerang bisa membaca informasi pribadi pengguna (nama, alamat, detail kartu kredit), mengubah isi halaman, atau bahkan melakukan tindakan atas nama pengguna.

❌ Ini menunjukkan bagaimana satu titik lemah (blog.example.com) bisa membahayakan seluruh ekosistem domain induk karena document.domain merusak batas keamanan yang seharusnya ada.

4. Alternatif Modern dan Aman untuk Komunikasi Cross-Origin

Untungnya, web modern menyediakan banyak API yang lebih aman dan terstruktur untuk komunikasi cross-origin. Mari kita bahas beberapa di antaranya:

4.1. Window.postMessage(): Komunikasi Aman Antar Window/Frame

Window.postMessage() adalah standar emas untuk komunikasi antar window, frame, atau worker dari origin yang berbeda. Ia memungkinkan pengiriman pesan secara asinkron dengan kontrol ketat atas origin pengirim dan penerima.

Cara Kerja:

Contoh Kode (postMessage):

// ====== Di Parent (misal: app.example.com) ======
const iframe = document.getElementById('myIframe');
const targetOrigin = 'https://api.example.com'; // ✅ Selalu tentukan origin spesifik

// Mengirim pesan ke iframe
iframe.contentWindow.postMessage({ type: 'DATA_REQUEST', payload: 'user_id_123' }, targetOrigin);

// Menerima pesan dari iframe
window.addEventListener('message', (event) => {
    // ⚠️ Sangat penting: Verifikasi origin pengirim!
    if (event.origin !== targetOrigin) {
        console.warn('Pesan diterima dari origin tidak dikenal:', event.origin);
        return;
    }

    if (event.data.type === 'DATA_RESPONSE') {
        console.log('Data dari iframe:', event.data.payload);
    }
});

// ====== Di Iframe (misal: api.example.com) ======
window.addEventListener('message', (event) => {
    // ⚠️ Sangat penting: Verifikasi origin pengirim!
    const expectedOrigin = 'https://app.example.com';
    if (event.origin !== expectedOrigin) {
        console.warn('Pesan diterima dari origin tidak dikenal:', event.origin);
        return;
    }

    if (event.data.type === 'DATA_REQUEST') {
        console.log('Menerima permintaan data:', event.data.payload);
        const responseData = { user: 'John Doe', balance: 1000 };
        // Mengirim balasan ke parent
        event.source.postMessage({ type: 'DATA_RESPONSE', payload: responseData }, event.origin);
    }
});

✅ postMessage adalah pilihan terbaik karena secara eksplisit memaksa Anda untuk mempertimbangkan dan memverifikasi origin,