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:
- Pencurian Data Sensitif: Cookie sesi, token autentikasi, data pribadi pengguna.
- Defacement Website: Mengubah tampilan halaman, memasang iklan palsu.
- Pengambilalihan Sesi: Menggunakan sesi pengguna yang sah untuk melakukan tindakan atas nama mereka.
- Distribusi Malware: Memaksa browser pengguna mengunduh file berbahaya.
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:
- Sink DOM Terproteksi: Beberapa properti DOM (seperti
innerHTML,outerHTML,srcuntukscripttag,hrefuntukatag denganjavascript:) secara otomatis akan menolak string mentah. - 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.
- 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:
require-trusted-types-for 'script': Ini adalah directive utama yang mengaktifkan Trusted Types. Kata kunci'script'berarti semua sink DOM yang dapat mengeksekusi JavaScript (sepertiinnerHTML,script.src) akan memerlukan Trusted Type. Anda juga bisa menggunakan'script-attr'untuk atribut event handler sepertionclick, atau'any'untuk semua sink.trusted-types default my-policy: Directive ini mendefinisikan Trusted Policies yang diizinkan untuk digunakan di halaman Anda.default: Ini adalah policy bawaan yang disediakan oleh browser. Biasanya, policydefaultini sangat ketat dan tidak mengizinkan banyak hal.my-policy: Ini adalah nama policy kustom yang akan Anda buat sendiri.
✅ 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:
'my-policy'adalah nama policy yang harus cocok dengan yang Anda deklarasikan di CSP (trusted-types default my-policy).- Objek kedua berisi fungsi-fungsi untuk membuat berbagai jenis objek terpercaya:
createHTML: UntukTrustedHTML, digunakan diinnerHTML,outerHTML.createScript: UntukTrustedScript, digunakan dieval(),setTimeout(),setInterval().createScriptURL: UntukTrustedScriptURL, digunakan discript.src,worker.src.createURL: UntukTrustedURL, digunakan dia.href,iframe.src, dll.
🎯 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:
- React
dangerouslySetInnerHTML: Jika Trusted Types aktif, Anda harus memberikan objekTrustedHTMLke__htmlproperti, bukan string mentah.function MyComponent({ htmlContent }) { const safeHtml = mySanitizerPolicy.createHTML(htmlContent); // Gunakan policy Anda return <div dangerouslySetInnerHTML={{ __html: safeHtml }} />; } - Angular: Angular memiliki sanitizernya sendiri yang menghasilkan
SafeHtml,SafeScript, dll. Versi Angular yang lebih baru sudah memiliki dukungan untuk Trusted Types, di manaSafeHtmlbisa menjadiTrustedHTMLsecara otomatis. - Vanilla JS/Library Lain: Pastikan semua kode yang memanipulasi DOM menggunakan Trusted Policies Anda. Ini mungkin memerlukan audit kode dan refactoring.
Tantangan dan Pertimbangan
- 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.
- 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-onlysangat penting di sini. - 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-scriptsdi CSP, tapi ini harus dilakukan dengan hati-hati. - 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.
- Debugging: Error DOMException yang muncul mungkin awalnya membingungkan. Gunakan Chrome DevTools (tab Security atau Console) untuk melihat detail pelanggaran.
🎯 Best Practice:
- Mulailah dengan
report-only: Jangan langsungenforcedi produksi. - Gunakan library sanitasi teruji: Jangan membuat sanitizer sendiri dari nol.
- Desain kebijakan yang minimalis: Hanya izinkan apa yang benar-benar diperlukan.
- Audit kode secara berkala: Pastikan tidak ada celah baru yang muncul.
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
- Mengamankan Aplikasi Web Anda dengan Content Security Policy (CSP): Panduan Praktis dan Best Practices
- Kriptografi di Browser dengan Web Crypto API: Mengamankan Data Sensitif di Sisi Klien
- Same-Origin Policy (SOP): Fondasi Keamanan Web yang Sering Terlupakan
- Membangun Sistem Anti-Bot dan Anti-Scraping: Melindungi Aplikasi Web Anda dari Lalu Lintas Jahat