Mulai Chrome 120, opsi unsanitized
baru tersedia di Async Clipboard API. Opsi ini dapat membantu dalam situasi khusus terkait HTML, yang mengharuskan Anda
menempelkan konten papan klip yang sama persis dengan konten saat disalin.
Artinya, tanpa langkah sanitasi menengah yang umumnya diterapkan browser
dan
untuk alasan yang baik. Pelajari cara menggunakannya dalam panduan ini.
Saat menggunakan Async Clipboard API, pada sebagian besar kasus, developer tidak perlu mengkhawatirkan integritas konten di papan klip dan dapat menganggap bahwa apa yang mereka tulis ke papan klip (salinan) adalah sama dengan yang akan didapatkan saat membaca data dari papan klip (menempel).
Hal ini jelas berlaku untuk teks. Coba tempelkan 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 persis sama 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);
Dengan gambar, terlihat 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);
Bagaimanapun, apa yang terjadi dengan teks HTML? Seperti yang Anda duga, dengan HTML, situasinya berbeda. Di sini, browser membersihkan kode HTML untuk mencegah hal-hal buruk terjadi, misalnya dengan menghapus tag <script>
dari kode HTML (dan lainnya seperti <meta>
, <head>
, dan <style>
) dan dengan membuat CSS menjadi inline.
Pertimbangkan contoh berikut dan cobalah di DevTools Console. Anda akan
melihat bahwa output sangat berbeda dengan input.
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);
Sanitasi HTML umumnya adalah hal yang baik. Anda tentu tidak ingin menempatkan diri Anda pada masalah keamanan dengan mengizinkan HTML yang tidak bersih dalam kebanyakan kasus. Namun, ada beberapa skenario saat developer tahu persis apa yang mereka lakukan dan di mana integritas HTML dalam dan output sangat penting untuk fungsi aplikasi yang benar. Dalam situasi ini, Anda memiliki dua pilihan:
- Jika Anda mengontrol penyalinan dan akhir penempelan, misalnya, jika Anda menyalin dari dalam aplikasi untuk kemudian menempel dalam aplikasi, Anda harus menggunakan Format kustom web untuk Async Clipboard API. Berhenti membaca di sini dan periksa artikel yang ditautkan.
- Jika Anda hanya mengontrol akhir penempelan di aplikasi, tetapi tidak mengakhiri penyalinan,
mungkin karena operasi penyalinan terjadi di aplikasi native yang tidak mendukung
format kustom web, Anda harus menggunakan opsi
unsanitized
, yang dijelaskan dalam sisa artikel ini.
Sanitasi mencakup hal-hal seperti menghapus tag script
, menyisipkan gaya, dan
memastikan HTML sudah tersusun dengan baik. Daftar ini tidak komprehensif, dan langkah-langkah
lainnya dapat ditambahkan di masa mendatang.
Salin dan tempel HTML yang bermasalah
Saat Anda write()
(menyalin) HTML ke papan klip dengan Async Clipboard API,
browser akan memastikan bahwa browser tersebut terbentuk dengan baik dengan menjalankannya melalui parser DOM
dan membuat serialisasi string HTML yang dihasilkan, tetapi tidak ada sanitasi yang terjadi pada
langkah ini. Anda tidak perlu melakukan apa pun. Jika HTML read()
ditempatkan pada
papan klip oleh aplikasi lain, dan aplikasi web Anda memilih untuk mendapatkan
konten fidelitas penuh dan perlu melakukan sanitasi apa pun dalam kode Anda sendiri,
Anda dapat meneruskan objek opsi ke metode read()
dengan properti
unsanitized
dan nilai ['text/html']
. Secara terpisah, formatnya terlihat seperti ini:
navigator.clipboard.read({ unsanitized: ['text/html'] })
. Contoh kode berikut
di bawah ini hampir sama dengan yang ditampilkan sebelumnya, tetapi kali ini dengan opsi
unsanitized
. Saat mencobanya di DevTools Console, Anda akan melihat bahwa input dan
outputnya 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 ini didukung, jadi deteksi
fitur didasarkan pada pengamatan perilaku. Oleh karena itu, contoh berikut
bergantung pada deteksi fakta apakah tag <style>
bertahan atau tidak, yang
menunjukkan dukungan, atau inline, yang menunjukkan non-dukungan. Perhatikan bahwa
agar hal ini berfungsi, halaman harus sudah 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 diuraikan dalam pengantar, sebagian besar developer tidak perlu mengkhawatirkan
sanitasi papan klip dan dapat menggunakan pilihan sanitasi default
yang dibuat oleh browser. Untuk kasus langka saat developer perlu menanganinya, opsi unsanitized
tersedia.
Link penting
Ucapan terima kasih
Artikel ini ditinjau oleh Anupam Snigdha dan Rachel Andrew. API ini ditentukan dan diimplementasikan oleh tim Microsoft Edge.