Mulai Chrome 120, opsi unsanitized
baru tersedia di Async Clipboard
API. Opsi ini dapat membantu dalam situasi khusus terkait HTML, saat Anda perlu
menempelkan konten papan klip seperti saat disalin.
Artinya, tanpa langkah pembersihan perantara yang biasanya diterapkan browser—dan
dengan alasan yang baik. Pelajari cara menggunakannya dalam panduan ini.
Saat menggunakan Async Clipboard API, dalam sebagian besar kasus, developer tidak perlu khawatir dengan integritas konten di papan klip dan dapat mengasumsikan bahwa apa yang mereka tulis ke papan klip (salin) sama dengan yang akan mereka dapatkan saat membaca data dari papan klip (tempel).
Hal ini berlaku untuk teks. Coba tempel kode berikut di Konsol DevTools, lalu segera fokuskan kembali halaman. (setTimeout()
diperlukan
agar Anda memiliki cukup waktu untuk memfokuskan halaman, yang merupakan persyaratan Async
Clipboard API.) Seperti yang Anda lihat, input sama persis dengan output.
setTimeout(async () => {
const input = 'Hello';
await navigator.clipboard.writeText(input);
const output = await navigator.clipboard.readText();
console.log(input, output, input === output);
// Logs "Hello Hello true".
}, 3000);
Untuk gambar, prosesnya sedikit berbeda. Untuk mencegah apa yang disebut serangan bom kompresi, browser mengenkode ulang gambar seperti PNG, tetapi gambar input dan output secara visual sama persis, piksel per piksel.
setTimeout(async () => {
const dataURL =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=';
const input = await fetch(dataURL).then((response) => response.blob());
await navigator.clipboard.write([
new ClipboardItem({
[input.type]: input,
}),
]);
const [clipboardItem] = await navigator.clipboard.read();
const output = await clipboardItem.getType(input.type);
console.log(input.size, output.size, input.type === output.type);
// Logs "68 161 true".
}, 3000);
Namun, apa yang terjadi dengan teks HTML? Seperti yang Anda duga, dengan HTML, situasinya
berbeda. Di sini, browser membersihkan kode HTML untuk mencegah hal buruk
terjadi, misalnya, menghapus tag <script>
dari kode HTML
(dan lainnya seperti <meta>
, <head>
, dan <style>
) dan dengan menyisipkan CSS.
Perhatikan contoh berikut dan coba di Konsol DevTools. Anda akan
melihat bahwa output sangat berbeda dengan inputnya.
setTimeout(async () => {
const input = `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="ProgId" content="Excel.Sheet" />
<meta name="Generator" content="Microsoft Excel 15" />
<style>
body {
font-family: HK Grotesk;
background-color: var(--color-bg);
}
</style>
</head>
<body>
<div>hello</div>
</body>
</html>`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read();
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
console.log(input, output);
}, 3000);
Pembersihan HTML umumnya merupakan hal yang baik. Anda tentu tidak ingin membuat diri Anda terpapar masalah keamanan dengan mengizinkan HTML yang bermasalah dalam sebagian besar kasus. Namun, ada skenario saat developer tahu persis apa yang mereka lakukan dan integritas HTML input dan output sangat penting untuk fungsi aplikasi yang benar. Dalam situasi ini, Anda memiliki dua pilihan:
- Jika Anda mengontrol akhir penyalinan dan penempelan, misalnya, jika Anda menyalin dari dalam aplikasi, lalu menempelkan dalam aplikasi, Anda harus menggunakan Format kustom web untuk Async Clipboard API. Berhenti membaca di sini dan periksa artikel tertaut.
- Jika Anda hanya mengontrol akhir proses menempel di aplikasi, tetapi tidak mengontrol akhir proses menyalin,
mungkin karena operasi salin terjadi di aplikasi native yang tidak mendukung
format kustom web, Anda harus menggunakan opsi
unsanitized
, yang dijelaskan di bagian lain artikel ini.
Sanitasi mencakup hal-hal seperti menghapus tag script
, menyisipkan gaya, dan
memastikan HTML tersusun dengan baik. Daftar ini tidak lengkap, dan lebih banyak
langkah dapat ditambahkan di masa mendatang.
Salin dan tempel HTML yang bermasalah
Saat Anda write()
(menyalin) HTML ke papan klip dengan Async Clipboard API, browser memastikan bahwa HTML tersebut terbentuk dengan baik dengan menjalankannya melalui parser DOM dan melakukan serialisasi string HTML yang dihasilkan, tetapi tidak ada pembersihan yang terjadi pada langkah ini. Anda tidak perlu melakukan apa pun. Jika Anda read()
HTML yang ditempatkan di
papan klip oleh aplikasi lain, dan aplikasi web Anda memilih untuk mendapatkan
konten fidelitas lengkap dan perlu melakukan pembersihan dalam kode Anda sendiri,
Anda dapat meneruskan objek opsi ke metode read()
dengan properti
unsanitized
dan nilai ['text/html']
. Secara terpisah, tampilannya seperti ini:
navigator.clipboard.read({ unsanitized: ['text/html'] })
. Contoh kode berikut
di bawah hampir sama dengan yang ditampilkan sebelumnya, tetapi kali ini dengan opsi
unsanitized
. Saat mencobanya di DevTools Console, Anda akan melihat bahwa input dan
output-nya sama.
setTimeout(async () => {
const input = `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="ProgId" content="Excel.Sheet" />
<meta name="Generator" content="Microsoft Excel 15" />
<style>
body {
font-family: HK Grotesk;
background-color: var(--color-bg);
}
</style>
</head>
<body>
<div>hello</div>
</body>
</html>`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read({
unsanitized: ['text/html'],
});
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
console.log(input, output);
}, 3000);
Dukungan browser dan deteksi fitur
Tidak ada cara langsung untuk memeriksa apakah fitur didukung, sehingga deteksi
fitur didasarkan pada pengamatan perilaku. Oleh karena itu, contoh berikut
bergantung pada deteksi fakta apakah tag <style>
tetap ada, yang
menunjukkan dukungan, atau di-inline, yang menunjukkan tidak adanya dukungan. Perhatikan bahwa
agar hal ini berfungsi, halaman sudah harus mendapatkan izin
papan klip.
const supportsUnsanitized = async () => {
const input = `<style>p{color:red}</style><p>a`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read({
unsanitized: ['text/html],
});
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
return /<style>/.test(output);
};
Demo
Untuk melihat cara kerja opsi unsanitized
, lihat
demo di Glitch dan lihat
kode sumbernya.
Kesimpulan
Seperti yang diuraikan dalam pengantar, sebagian besar developer tidak perlu mengkhawatirkan
sanitasi papan klip dan hanya perlu menangani pilihan sanitasi default
yang dibuat oleh browser. Untuk kasus yang jarang terjadi saat developer perlu memperhatikan,
opsi unsanitized
ada.
Link penting
Ucapan terima kasih
Artikel ini ditinjau oleh Anupam Snigdha dan Rachel Andrew. API ini ditentukan dan diimplementasikan oleh tim Microsoft Edge.