Mencegah kerentanan pembuatan skrip lintas situs berbasis DOM dengan Jenis Tepercaya

Krzysztof Kotowicz
Krzysztof Kotowicz

Dukungan Browser

  • 83
  • 83
  • x
  • x

Sumber

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 merupakan hal yang umum bagi tim developer secara tidak sengaja memperkenalkannya di aplikasi mereka. Jenis Tepercaya memberi Anda alat untuk menulis, meninjau keamanan, dan menjaga aplikasi 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 secara tidak aman menciptakan kode HTML yang membentuk situs. Fungsi lain memiliki penyebab utama pada klien, yaitu kode JavaScript memanggil fungsi berbahaya dengan konten yang dikontrol pengguna.

Untuk mencegah XSS sisi server, jangan buat HTML dengan menyambungkan string. Sebagai gantinya, gunakan library template autoescaping kontekstual yang aman, bersama dengan Kebijakan Keamanan Konten berbasis nonce untuk mitigasi bug tambahan.

Kini browser juga dapat membantu mencegah XSS berbasis DOM sisi klien dengan menggunakan Jenis Tepercaya.

Pengantar API

Jenis Tepercaya bekerja dengan mengunci fungsi sink berisiko berikut. Anda mungkin sudah mengenali sebagian di antaranya, karena vendor browser dan framework web sudah mengarahkan Anda untuk tidak menggunakan fitur ini demi alasan keamanan.

Jenis Tepercaya mengharuskan Anda memproses data sebelum meneruskannya ke fungsi sink ini. Penggunaan string saja akan gagal, karena browser tidak tahu apakah data tersebut dapat dipercaya:

Larangan
anElement.innerHTML  = location.href;
Jika Jenis Tepercaya diaktifkan, browser akan menampilkan TypeError dan mencegah penggunaan sink DOM XSS dengan string.

Untuk menunjukkan bahwa data telah diproses dengan aman, buat objek khusus - Jenis Tepercaya.

Anjuran
anElement.innerHTML = aTrustedHTML;
  
Dengan mengaktifkan Jenis Tepercaya, browser akan menerima objek TrustedHTML untuk sink yang meminta cuplikan HTML. Ada juga objek TrustedScript dan TrustedScriptURL untuk sink sensitif lainnya.

Jenis Tepercaya mengurangi permukaan serangan DOM XSS aplikasi Anda secara signifikan. Layanan ini menyederhanakan peninjauan keamanan, dan memungkinkan Anda menerapkan pemeriksaan keamanan berbasis jenis yang dilakukan saat mengompilasi, melakukan analisis lint, atau memaketkan kode Anda saat runtime, di browser.

Cara menggunakan Jenis Tepercaya

Bersiap untuk laporan pelanggaran Kebijakan Keamanan Konten

Anda dapat men-deploy pengumpul laporan, seperti reporting-api-processor open source atau go-csp-collector, atau menggunakan salah satu platform komersial yang setara. 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 terus berfungsi. Bagian berikutnya menjelaskan cara kerja //my-csp-endpoint.example.

Mengidentifikasi pelanggaran Jenis Tepercaya

Mulai sekarang, setiap kali Jenis Tepercaya mendeteksi pelanggaran, browser akan mengirimkan laporan ke report-uri yang dikonfigurasi. Misalnya, saat aplikasi Anda meneruskan string ke innerHTML, browser akan mengirim 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"
}
}

Artinya, dalam https://my.url.example/script.js di baris 39, innerHTML dipanggil dengan string yang diawali dengan <img src=x. Informasi ini akan membantu Anda mempersempit bagian kode mana yang mungkin memperkenalkan 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

Ada kemungkinan kode yang tidak sesuai tidak diperlukan lagi, atau dapat ditulis ulang tanpa fungsi yang menyebabkan pelanggaran:

Anjuran
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
Larangan
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, serta menghapus payload XSS.

import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});

DOMPurify mendukung Jenis Tepercaya dan menampilkan HTML bersih yang 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 sendiri objek Jenis Tepercaya.

Pertama, buat kebijakan. Kebijakan adalah setelan pabrik 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, '&lt;')
  });
}

Kode ini membuat kebijakan bernama myEscapePolicy yang dapat menghasilkan objek TrustedHTML menggunakan fungsi createHTML(). Karakter < escape HTML aturan yang ditentukan 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;  // '&lt;img src=x onerror=alert(1)>'

Gunakan 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 di sink yang hanya menerima Jenis Tepercaya.

Beralih ke penerapan Kebijakan Keamanan Konten

Jika aplikasi tidak lagi menghasilkan pelanggaran, Anda dapat mulai menerapkan Jenis Tepercaya:

Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

Sekarang, tidak peduli seberapa rumit aplikasi web Anda, satu-satunya hal yang dapat menyebabkan kerentanan DOM XSS adalah kode dalam salah satu kebijakan Anda, dan Anda dapat menguncinya lebih jauh dengan membatasi pembuatan kebijakan.

Bacaan lebih lanjut