Pembuatan skrip lintas situs berbasis DOM (DOM XSS) terjadi saat data dari
sumber yang dikontrol pengguna (seperti nama pengguna, atau URL alihan yang diambil dari fragmen
URL) mencapai sink, yang merupakan fungsi seperti eval()
atau penyetel
properti seperti .innerHTML
yang dapat mengeksekusi kode JavaScript arbitrer.
DOM XSS adalah salah satu kerentanan keamanan web yang paling umum, dan tim developer sering kali tidak sengaja memasukkannya ke dalam aplikasi mereka. Trusted Types memberi Anda alat untuk menulis, meninjau keamanan, dan menjaga aplikasi agar bebas dari kerentanan DOM XSS dengan membuat fungsi API web berbahaya aman secara default. Jenis Tepercaya tersedia sebagai polyfill untuk browser yang belum mendukungnya.
Latar belakang
Selama bertahun-tahun, DOM XSS telah menjadi salah satu kerentanan keamanan web yang paling umum dan berbahaya.
Ada dua jenis pembuatan skrip lintas situs. Beberapa kerentanan XSS disebabkan oleh kode sisi server yang membuat kode HTML yang membentuk situs dengan tidak aman. Yang lainnya memiliki akar masalah di klien, tempat kode JavaScript memanggil fungsi berbahaya dengan konten yang dikontrol pengguna.
Untuk mencegah XSS sisi server, jangan buat HTML dengan menggabungkan string. Sebagai gantinya, gunakan library template pengelakan otomatis kontekstual yang aman, bersama dengan Kebijakan Keamanan Konten berbasis nonce untuk mitigasi bug tambahan.
Sekarang browser juga dapat membantu mencegah XSS berbasis DOM sisi klien dengan menggunakan Jenis Tepercaya.
Pengantar API
Jenis Tepercaya berfungsi dengan mengunci fungsi sink berisiko berikut. Anda mungkin sudah mengenali beberapa di antaranya, karena vendor browser dan framework web telah mengarahkan Anda untuk tidak menggunakan fitur ini karena alasan keamanan.
- Manipulasi skrip:
<script src>
dan menetapkan konten teks elemen<script>
. - Membuat HTML dari string:
- Menjalankan konten plugin:
- Kompilasi kode JavaScript runtime:
eval
setTimeout
setInterval
new Function()
Jenis Tepercaya mengharuskan Anda memproses data sebelum meneruskannya ke fungsi sink ini. Hanya menggunakan string akan gagal, karena browser tidak tahu apakah data tersebut dapat dipercaya:
anElement.innerHTML = location.href;
Untuk menunjukkan bahwa data diproses dengan aman, buat objek khusus - Jenis Tepercaya.
anElement.innerHTML = aTrustedHTML;
Jenis Tepercaya secara signifikan mengurangi platform serangan DOM XSS aplikasi Anda. Hal ini menyederhanakan peninjauan keamanan, dan memungkinkan Anda menerapkan pemeriksaan keamanan berbasis jenis yang dilakukan saat mengompilasi, melakukan linting, atau memaketkan kode saat runtime, di browser.
Cara menggunakan Jenis Tepercaya
Bersiap menghadapi laporan pelanggaran Kebijakan Keamanan Konten
Anda dapat men-deploy kolektor laporan, seperti reporting-api-processor atau go-csp-collector open source, atau menggunakan salah satu yang setara secara komersial. Anda juga dapat menambahkan logging kustom dan pelanggaran debug di browser menggunakan ReportingObserver:
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
atau dengan menambahkan pemroses peristiwa:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
Menambahkan header CSP khusus laporan
Tambahkan header Respons HTTP berikut ke dokumen yang ingin Anda migrasikan ke Jenis Tepercaya:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Sekarang semua pelanggaran dilaporkan ke //my-csp-endpoint.example
, tetapi situs
tetap berfungsi. Bagian berikutnya menjelaskan cara kerja
//my-csp-endpoint.example
.
Mengidentifikasi pelanggaran Jenis Tepercaya
Mulai sekarang, setiap kali Jenis Tepercaya mendeteksi pelanggaran, browser akan mengirim
laporan ke report-uri
yang dikonfigurasi. Misalnya, saat aplikasi Anda
meneruskan string ke innerHTML
, browser akan mengirimkan laporan berikut:
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
Ini menunjukkan bahwa di https://my.url.example/script.js
pada baris 39, innerHTML
dipanggil dengan string yang dimulai dengan <img src=x
. Informasi ini akan membantu
Anda mempersempit bagian kode yang mungkin menyebabkan DOM XSS dan perlu diubah.
Memperbaiki pelanggaran
Ada beberapa opsi untuk memperbaiki pelanggaran Jenis Tepercaya. Anda dapat menghapus kode yang melanggar, menggunakan library, membuat kebijakan Jenis Tepercaya, atau sebagai upaya terakhir, membuat kebijakan default.
Menulis ulang kode yang melanggar
Mungkin kode yang tidak sesuai tidak diperlukan lagi, atau dapat ditulis ulang tanpa fungsi yang menyebabkan pelanggaran:
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
Menggunakan library
Beberapa library sudah menghasilkan Jenis Tepercaya yang dapat Anda teruskan ke fungsi sink. Misalnya, Anda dapat menggunakan DOMPurify untuk membersihkan cuplikan HTML, menghapus payload XSS.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify mendukung Jenis Tepercaya
dan menampilkan HTML yang dibersihkan dan digabungkan dalam objek TrustedHTML
sehingga browser
tidak menghasilkan pelanggaran.
Membuat kebijakan Jenis Tepercaya
Terkadang Anda tidak dapat menghapus kode yang menyebabkan pelanggaran, dan tidak ada library untuk membersihkan nilai dan membuat Jenis Tepercaya untuk Anda. Dalam kasus tersebut, Anda dapat membuat objek Jenis Tepercaya sendiri.
Pertama, buat kebijakan. Kebijakan adalah factory untuk Jenis Tepercaya yang menerapkan aturan keamanan tertentu pada inputnya:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
Kode ini membuat kebijakan yang disebut myEscapePolicy
yang dapat menghasilkan objek TrustedHTML
menggunakan fungsi createHTML()
-nya. Aturan yang ditentukan akan meng-escape karakter <
HTML
untuk mencegah pembuatan elemen HTML baru.
Gunakan kebijakan seperti ini:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
Menggunakan kebijakan default
Terkadang Anda tidak dapat mengubah kode yang melanggar, misalnya, jika Anda memuat library pihak ketiga dari CDN. Dalam hal ini, gunakan kebijakan default:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
Kebijakan bernama default
digunakan di mana pun string digunakan dalam sink yang hanya
menerima Jenis Tepercaya.
Beralih untuk menerapkan Kebijakan Keamanan Konten
Jika aplikasi Anda tidak lagi menghasilkan pelanggaran, Anda dapat mulai menerapkan Trusted Types:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Sekarang, tidak peduli seberapa kompleks aplikasi web Anda, satu-satunya hal yang dapat menyebabkan kerentanan DOM XSS adalah kode di salah satu kebijakan Anda, dan Anda dapat menguncinya lebih lanjut dengan membatasi pembuatan kebijakan.