WEB-SECURITY SECURITY DOM-XSS XSS TRUSTED-TYPES BROWSER-API CONTENT-SECURITY-POLICY CSP FRONTEND-SECURITY JAVASCRIPT BEST-PRACTICES VULNERABILITY-PREVENTION

Mengamankan Aplikasi Web dari DOM XSS dengan Trusted Types: Jurus Ampuh Melawan Serangan Tersembunyi

⏱️ 10 menit baca
👨‍💻

Mengamankan Aplikasi Web dari DOM XSS dengan Trusted Types: Jurus Ampuh Melawan Serangan Tersembunyi

1. Pendahuluan

Di dunia pengembangan web yang serba cepat ini, keamanan adalah prioritas utama. Kita sering berbicara tentang SQL Injection, CSRF, atau XSS di sisi server. Namun, ada satu ancaman yang tak kalah berbahaya dan sering luput dari perhatian, terutama di aplikasi web modern yang sarat JavaScript: DOM Cross-Site Scripting (DOM XSS).

DOM XSS terjadi ketika data yang dikontrol penyerang dimanipulasi oleh JavaScript di browser klien dan dieksekusi sebagai kode. Bayangkan, sebuah skrip jahat bisa disuntikkan ke halaman Anda tanpa pernah menyentuh server! Ini bisa mencuri data sensitif, membajak sesi pengguna, atau bahkan mengubah tampilan website Anda.

Mencegah DOM XSS bisa jadi tantangan berat. Sanitasi input pengguna saja tidak cukup jika ada bagian dari kode JavaScript Anda sendiri yang secara tidak sengaja memasukkan string yang tidak aman ke dalam DOM. Di sinilah Trusted Types hadir sebagai pahlawan.

Trusted Types adalah fitur keamanan browser modern yang dirancang untuk mengunci sink DOM yang rentan XSS. Ia memastikan bahwa data yang dimasukkan ke dalam elemen-elemen berbahaya (seperti innerHTML, document.write, script tag) harus berupa objek “terpercaya” yang telah melewati proses sanitasi atau dibuat oleh Trusted Policy yang aman. Mari kita selami bagaimana Trusted Types bisa menjadi benteng pertahanan terakhir Anda melawan DOM XSS.

2. Apa Itu DOM XSS dan Mengapa Berbahaya?

Sebelum kita masuk ke Trusted Types, mari kita pahami kembali DOM XSS.

📌 DOM XSS: Serangan di Sisi Klien Berbeda dengan XSS Reflected atau Stored yang melibatkan server, DOM XSS terjadi sepenuhnya di sisi klien. Ini bermula ketika aplikasi web mengambil data dari sumber yang tidak terpercaya (misalnya, URL, localStorage, postMessage) dan kemudian menuliskannya ke sink DOM yang berbahaya tanpa sanitasi yang memadai.

Contoh Sederhana DOM XSS:

// Ambil parameter 'q' dari URL
const params = new URLSearchParams(window.location.search);
const query = params.get('q'); // Misalnya URL: ?q=<img src=x onerror=alert(document.domain)>

// Masukkan langsung ke DOM tanpa sanitasi
document.getElementById('search-results').innerHTML = `Hasil pencarian untuk: ${query}`;
// Jika 'query' berisi payload XSS, maka skrip akan dieksekusi!

Dalam contoh di atas, jika query berisi "<img src=x onerror=alert(document.domain)>", maka browser akan menganggapnya sebagai bagian dari HTML dan menjalankan skrip di dalam onerror. Penyerang bisa mengganti alert(document.domain) dengan kode JavaScript apa pun yang mereka inginkan.

⚠️ Bahaya DOM XSS:

Meskipun developer berhati-hati, kesalahan kecil dalam penanganan string bisa membuka celah. Di sinilah Trusted Types menawarkan solusi yang lebih kuat dan default-safe.

3. Memperkenalkan Trusted Types: Perisai Otomatis di Browser

Trusted Types adalah Web API yang dirancang untuk mencegah DOM XSS dengan memaksa developer untuk memproses data yang akan masuk ke sink DOM tertentu. Alih-alih mengizinkan string mentah, Trusted Types mengharuskan data berupa objek khusus (TrustedHTML, TrustedScript, TrustedScriptURL, TrustedURL) yang hanya bisa dibuat oleh Trusted Policy yang terdefinisi.

💡 Konsep Inti Trusted Types:

  1. Sink DOM Terproteksi: Beberapa properti DOM (seperti innerHTML, outerHTML, src untuk script tag, href untuk a tag dengan javascript:) secara otomatis akan menolak string mentah.
  2. Trusted Policy: Fungsi JavaScript yang Anda tulis untuk secara eksplisit membuat objek “terpercaya” dari string mentah. Policy ini bertanggung jawab untuk melakukan sanitasi atau validasi.
  3. Objek Terpercaya: Objek khusus (TrustedHTML, dll.) yang dihasilkan oleh Trusted Policy, yang diizinkan untuk masuk ke sink DOM terproteksi.

Dengan Trusted Types, Anda tidak lagi bergantung pada developer untuk selalu ingat melakukan sanitasi manual di setiap titik. Browser akan secara otomatis memblokir injeksi string berbahaya jika tidak berasal dari Trusted Policy. Ini adalah pendekatan deny-by-default yang secara signifikan meningkatkan keamanan.

4. Mengaktifkan Trusted Types

Trusted Types tidak aktif secara default. Anda harus mengaktifkannya melalui Content Security Policy (CSP).

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default my-policy;

Mari kita bedah bagian ini:

Mode report-only vs enforce: Ketika pertama kali mengimplementasikan Trusted Types, sangat disarankan untuk menggunakan mode report-only di CSP Anda:

Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; trusted-types default my-policy;

Dalam mode ini, browser akan melaporkan pelanggaran ke endpoint yang Anda tentukan (report-uri atau report-to di CSP) tanpa memblokir skrip. Ini memungkinkan Anda mengidentifikasi semua bagian kode yang perlu diadaptasi tanpa merusak fungsionalitas aplikasi Anda. Setelah yakin tidak ada pelanggaran, Anda bisa beralih ke mode enforce dengan menghapus -Report-Only.

5. Memahami Trusted Policies dan Objek Terpercaya

Setelah diaktifkan, setiap kali Anda mencoba memasukkan string ke sink DOM yang dilindungi, browser akan memunculkan error, misalnya:

DOMException: Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment.

Untuk mengatasinya, Anda perlu membuat dan menggunakan Trusted Policies.

Membuat Trusted Policies

Anda membuat Trusted Policy menggunakan window.trustedTypes.createPolicy():

// Pastikan Trusted Types didukung dan aktif
if (window.trustedTypes && trustedTypes.createPolicy) {
  const mySanitizerPolicy = trustedTypes.createPolicy('my-policy', {
    createHTML: (input) => {
      // ✅ Ini adalah tempat Anda melakukan sanitasi string input
      // Misalnya, menggunakan DOMPurify atau logika sanitasi kustom yang teruji
      // Untuk demo, kita asumsikan input sudah aman atau kita lakukan sanitasi minimal
      console.log('Sanitizing HTML:', input);
      return input; // Dalam aplikasi nyata, ini harus disanitasi!
    },
    createScript: (input) => {
      console.log('Sanitizing Script:', input);
      return input; // Hati-hati di sini, pastikan skrip aman!
    },
    createScriptURL: (input) => {
      console.log('Sanitizing Script URL:', input);
      // Anda bisa membatasi URL hanya dari domain tertentu
      if (input.startsWith('https://cdn.example.com/')) {
        return input;
      }
      throw new Error('Script URL tidak diizinkan');
    },
    // createURL: (input) => { ... } // Untuk TrustedURL
  });

  // Sekarang, gunakan policy ini untuk membuat TrustedHTML
  const safeHtml = mySanitizerPolicy.createHTML('<h1>Halo, Dunia!</h1><script>alert(1)</script>');
  document.getElementById('app').innerHTML = safeHtml; // ✅ Ini akan lolos!

  // ❌ Ini akan gagal karena masih string mentah
  // document.getElementById('app').innerHTML = '<h1>Halo, Dunia!</h1>';
} else {
  console.warn('Trusted Types tidak didukung atau tidak aktif.');
  // Fallback atau peringatan
}

Dalam contoh di atas:

🎯 Penting: Fungsi createHTML, createScript, dll., adalah tempat Anda benar-benar melakukan sanitasi atau validasi. Jika Anda hanya mengembalikan input secara langsung tanpa proses yang aman, Anda masih rentan terhadap XSS. Gunakan library sanitasi yang teruji seperti DOMPurify di dalam createHTML Anda.

// Contoh dengan DOMPurify (pastikan DOMPurify sudah di-load)
const mySanitizerPolicy = trustedTypes.createPolicy('my-policy', {
  createHTML: (input) => DOMPurify.sanitize(input, { RETURN_TRUSTED_TYPE: true }),
});

DOMPurify memiliki opsi RETURN_TRUSTED_TYPE yang secara langsung mengembalikan objek TrustedHTML jika Trusted Types aktif, membuatnya sangat mudah diintegrasikan.

6. Integrasi dengan Library/Framework Populer dan Pertimbangan

Integrasi di Aplikasi Modern

Banyak framework modern seperti React, Angular, dan Vue sudah memiliki mekanisme perlindungan bawaan terhadap XSS (misalnya, React secara otomatis meng-escape string yang dimasukkan ke JSX). Namun, ini tidak selalu mencakup semua skenario, terutama jika Anda menggunakan properti seperti dangerouslySetInnerHTML di React.

Dengan Trusted Types, Anda bisa memberikan lapisan keamanan ekstra:

Tantangan dan Pertimbangan

  1. Dukungan Browser: Trusted Types didukung di browser modern berbasis Chromium (Chrome, Edge, Opera) dan Firefox (sejak versi 113). Safari masih dalam tahap pengembangan. Pastikan untuk menyediakan fallback atau membatasi penggunaan fitur ini untuk browser yang didukung jika Anda membutuhkan kompatibilitas luas.
  2. Migrasi Aplikasi Lama: Mengimplementasikan Trusted Types pada aplikasi yang sudah besar bisa menjadi pekerjaan yang signifikan. Anda perlu mengidentifikasi semua titik di mana string dimasukkan ke sink DOM dan mengadaptasinya untuk menggunakan Trusted Policies. Mode report-only sangat penting di sini.
  3. Pihak Ketiga (Third-Party Scripts): Skrip pihak ketiga (iklan, analitik, widget) mungkin tidak kompatibel dengan Trusted Types dan bisa memicu pelanggaran. Anda perlu mengevaluasi setiap skrip pihak ketiga. Terkadang, Anda mungkin perlu mengecualikan domain tertentu dari Trusted Types menggunakan directive unsafe-allow-untrusted-scripts di CSP, tapi ini harus dilakukan dengan hati-hati.
  4. Performa: Dampak performa dari Trusted Types sangat minimal karena sebagian besar pemrosesan terjadi di level browser. Namun, kompleksitas logika sanitasi di dalam Trusted Policy Anda bisa memengaruhi performa.
  5. Debugging: Error DOMException yang muncul mungkin awalnya membingungkan. Gunakan Chrome DevTools (tab Security atau Console) untuk melihat detail pelanggaran.

🎯 Best Practice:

Kesimpulan

DOM XSS adalah ancaman nyata yang bisa menyelinap melalui celah keamanan aplikasi web modern. Trusted Types menawarkan solusi yang elegan dan kuat dengan pendekatan deny-by-default, memaksa developer untuk secara eksplisit memvalidasi atau mensanitasi setiap string yang akan masuk ke sink DOM yang berbahaya.

Meskipun implementasinya mungkin memerlukan upaya di awal, terutama untuk aplikasi yang sudah ada, manfaat keamanan yang ditawarkannya sangat besar. Dengan Trusted Types, Anda tidak hanya mencegah serangan XSS yang merusak, tetapi juga membangun fondasi kode yang lebih aman dan tangguh, memberikan ketenangan pikiran bagi Anda dan pengguna Anda.

Mari kita jadikan Trusted Types sebagai bagian standar dari toolkit keamanan frontend kita.

🔗 Baca Juga