4 poin oleh GN⁺ 2024-09-02 | 1 komentar | Bagikan ke WhatsApp
  • Clipboard web menyimpan data yang sudah disalin sekali dalam beberapa representasi MIME sekaligus, sehingga tiap aplikasi bisa memilih hanya bentuk yang dibutuhkannya saat menempel
  • async Clipboard API dibatasi terutama pada text/plain, text/html, dan image/png, sehingga tipe kustom seperti application/json ditolak saat ditulis karena alasan keamanan
  • Clipboard Events API yang lebih lama dapat menangani string tipe arbitrer, tetapi clipboard sistem sebenarnya hanya berubah di dalam event pengguna tepercaya
  • Google Docs membuat event copy tepercaya dengan execCommand("copy"), sementara Figma memasukkan data kustom ke dalam atribut HTML base64 untuk menangani penyalinan antara browser dan aplikasi native
  • Web Custom Formats membuka tipe kustom pada async Clipboard API dengan prefiks "web ", tetapi membutuhkan browser berbasis Chromium dan aplikasi native yang sudah diperbarui

Model dasar clipboard web

  • Clipboard dapat menyimpan beberapa representasi (representation) beserta tipe MIME untuk satu operasi penyalinan
    • text/plain: teks biasa
    • text/html: HTML
    • image/png: gambar PNG
  • Google Docs membaca representasi text/html untuk mempertahankan pemformatan seperti tautan, ukuran huruf, dan warna
  • Aplikasi yang hanya membutuhkan teks sumber seperti VS Code cukup membaca representasi text/plain

Batasan tipe pada async Clipboard API

  • navigator.clipboard.read() memeriksa types dari item clipboard lalu membaca representasi tertentu seperti getType("text/html")
  • navigator.clipboard.write() dapat menulis beberapa Blob sekaligus dalam ClipboardItem yang memakai tipe MIME sebagai kunci
  • Jika mencoba menulis application/json sebagai ClipboardItem, akan muncul exception Type application/json not supported on write
  • Spesifikasi W3C Clipboard mengharuskan tipe selain text/plain, text/html, dan image/png ditolak pada write
  • application/json pernah ada dalam daftar tipe data wajib dari 2012 hingga 2021, tetapi dihapus dalam w3c/clipboard-apis#155
    • Sebelum perubahan, tipe data wajib berjumlah 16 untuk baca dan 8 untuk tulis
    • Daftar itu dipangkas karena browser tidak mendukung banyak tipe wajib akibat kekhawatiran keamanan
  • Spesifikasi memperingatkan bahwa skrip tidak tepercaya dapat memasukkan data ke clipboard yang memicu kerentanan perangkat lunak lokal, sehingga tipe yang diizinkan dibatasi

Clipboard Events API dan isTrusted

  • ClipboardEvent terjadi pada event copy, cut, dan paste, serta menyediakan objek DataTransfer melalui properti clipboardData
  • Pada event copy, data dapat ditulis dengan e.clipboardData.setData("text/plain", "...") dan setData("text/html", "...")
  • Pada event paste, representasi HTML dapat dibaca dengan e.clipboardData.getData("text/html")
  • clipboardData.setData("application/json", json) tidak melempar exception, dan handler paste juga dapat melihat tipe application/json beserta string JSON-nya
  • clipboardData adalah antarmuka yang jauh lebih tua daripada async Clipboard API
    • async Clipboard API ditambahkan ke spesifikasi pada 2017
    • clipboardData, setData, dan getData sudah muncul dalam draf W3C tahun 2006
    • Draf tersebut banyak merefleksikan fitur yang diimplementasikan di Internet Explorer
    • Karena Internet Explorer 4 dirilis pada 1997, antarmuka clipboardData dapat dianggap berusia setidaknya 26 tahun pada saat artikel ditulis
  • Sejak spesifikasi 2011, tipe MIME mulai masuk, tetapi sifat bahwa argumen tipe pada setData() dapat berisi string apa pun masih dipertahankan hingga sekarang
  • Event.isTrusted adalah properti read-only yang bernilai true hanya ketika event dibuat oleh user agent
    • Event copy ketika pengguna menekan Command+C memiliki isTrusted bernilai true
    • Event sintetis yang dibuat dengan dispatchEvent() memiliki isTrusted bernilai false
  • Event copy/cut sintetis tidak dapat mengubah clipboard sistem, dan event paste sintetis tidak dapat mengakses data clipboard sistem yang sebenarnya
  • Karena itu, Clipboard Events API dapat menangani tipe arbitrer, tetapi interaksi dengan clipboard sebenarnya dibatasi pada handler event copy/paste yang dibuat oleh user agent

Menulis tipe kustom dari tombol Copy

  • Tombol Copy pada menu klik kanan Google Docs menulis tiga representasi ke clipboard
    • text/plain
    • text/html
    • application/x-vnd.google-docs-document-slice-clip+wrapped
  • Representasi ketiga adalah tipe kustom berisi data JSON, dan tidak dapat ditulis hanya dengan async Clipboard API
  • Google Docs memanggil document.execCommand("copy") saat tombol Copy diklik
  • execCommand("copy") dapat secara programatik memunculkan event copy tepercaya seolah pengguna menjalankan perintah salin
  • Di Safari, execCommand("copy") membutuhkan area seleksi aktif agar event copy muncul
    • Area seleksi dapat dibuat dengan menambahkan input yang tidak kosong ke DOM, memilihnya, lalu memanggil execCommand("copy") dan menghapusnya

Perbedaan browser dan OS pada tombol Paste

  • Tombol Paste di Google Docs meminta pemasangan ekstensi di macOS, tetapi berfungsi di laptop Windows
  • Dukungan perintah paste dapat diperiksa dengan document.queryCommandSupported("paste")
    • Di macOS, Chrome dan Firefox bernilai false, Safari bernilai true
    • Di Windows, Chrome dan Edge bernilai true, Firefox bernilai false
  • Safari menampilkan prompt untuk mengonfirmasi aksi paste, sehingga jelas bahwa situs web membaca clipboard
  • Alasan Chrome mengizinkan execCommand("paste") di Windows tetapi tidak di macOS belum terkonfirmasi
  • Google Docs tidak fallback ke async Clipboard API saat execCommand("paste") tidak dapat digunakan
    • async Clipboard API tidak dapat membaca representasi application/x-vnd.google-[...]
    • Representasi HTML berisi ID internal

Copy-paste berbasis HTML di Figma

  • Tombol Copy Figma menulis dua representasi ke clipboard: text/plain dan text/html
  • Representasi HTML berisi dua span kosong, masing-masing memiliki atribut data-metadata dan data-buffer
  • data-metadata adalah string JSON yang di-encode base64; setelah di-decode, muncul nilai seperti fileKey, pasteID, dan dataType
  • data-buffer berukuran sekitar 26.000 karakter bahkan saat hanya menyalin frame kosong, dan panjangnya tampak bertambah secara linear sesuai jumlah konten yang disalin
  • Jika data-buffer di-decode dari base64, muncul data biner yang diawali fig-kiwi; ini adalah Kiwi message format
    • Kiwi adalah format yang dibuat oleh Evan Wallace, salah satu pendiri Figma dan mantan CTO-nya
    • Digunakan untuk encoding file .fig
  • Dengan memakai parser file .fig yang dipublikasikan Evan Wallace, data-buffer dapat dikonversi menjadi file .fig untuk diperiksa
  • Figma membuat file Figma kecil, meng-encode-nya ke base64, lalu memasukkannya ke atribut data-buffer pada span HTML kosong untuk disimpan di clipboard

Mengapa memakai representasi HTML

  • Windows, macOS, dan Linux menyediakan format OS yang berbeda untuk menulis data ke clipboard
    • Windows menyediakan CF_HTML untuk HTML
    • macOS menyediakan NSPasteboard.PasteboardType.html
  • Teks biasa, HTML, dan gambar PNG dapat dipetakan ke format standar di berbagai OS
  • Tipe arbitrer seperti application/foo-bar tidak cocok dengan format umum clipboard OS
  • Browser tidak menulis representasi kustom seperti ini ke format clipboard umum milik OS, melainkan menyimpannya di dalam format clipboard kustom per browser
    • Akibatnya, tipe kustom dapat di-copy-paste antar-tab browser
    • Namun tidak dapat di-copy-paste antar-aplikasi
  • Dengan memakai text/html, data dipetakan ke format clipboard umum OS sehingga lebih mudah dibaca aplikasi lain
  • Figma memakai text/html agar elemen dapat di-copy-paste antara figma.com di browser dan aplikasi Figma native

Cara tiap browser menyimpan data kustom

  • Di macOS, jika text/plain, text/html, application/json, dan foo bar baz ditulis dalam event copy lalu diperiksa dengan Pasteboard Viewer, cara penyimpanannya berbeda di tiap browser
  • Chrome menambahkan empat item ke Pasteboard
    • public.html: representasi HTML
    • public.utf8-plain-text: representasi teks biasa
    • org.chromium.web-custom-data: representasi kustom
    • org.chromium.source-url: URL halaman web tempat penyalinan dilakukan
  • Firefox juga membuat public.html dan public.utf8-plain-text, tetapi menulis data kustom ke org.mozilla.custom-clipdata
    • Berbeda dari Chrome, Firefox tidak menyimpan source URL
  • Safari juga membuat public.html dan public.utf8-plain-text, serta menulis data kustom ke com.apple.WebKit.custom-pasteboard-data
    • Di dalam item ini, Safari juga menyimpan daftar seluruh representasi termasuk teks biasa dan HTML, serta source URL
  • Safari mengizinkan copy-paste tipe kustom antar-tab browser hanya jika domain source URL sama
    • Di Chrome dan Firefox, batasan yang sama tidak terlihat

Raw Clipboard Access dan Web Custom Formats

  • Raw Clipboard Access adalah API yang diusulkan pada 2019 untuk memberi aplikasi web akses baca-tulis mentah ke clipboard OS native
  • Raw Clipboard Access menangani masalah bahwa aplikasi web dibatasi pada sedikit format, sehingga Figma dan Photopea sulit berinteroperasi dengan sebagian besar format gambar
  • Proposal ini tidak berlanjut lebih jauh karena kekhawatiran keamanan seperti eksekusi kode jarak jauh pada aplikasi native
  • Web Custom Formats adalah proposal untuk memakai tipe kustom di async Clipboard API, dan Chromium mengimplementasikannya pada 2022
  • Web Custom Formats digunakan dengan menambahkan "web " di depan tipe MIME
    • Contoh: web application/json
    • Saat membaca, gunakan juga read() dari async Clipboard API dan getType("web application/json")
  • Web Custom Formats menulis dua jenis data ke clipboard OS native
    • Pemetaan antara tipe data dan nama item clipboard
    • Item clipboard untuk tiap tipe data
  • Di macOS, pemetaan disimpan di org.w3.web-custom-format.map
    • Contoh: "application/json": "org.w3.web-custom-format.type-0"
  • Item org.w3.web-custom-format.type-[index] berisi data mentah dari Blob
  • Cara ini menghindari masalah keamanan Raw Clipboard Access dengan mencegah aplikasi web menulis data mentah langsung ke format clipboard OS yang diinginkannya
  • Sebagai gantinya, interoperabilitas dengan aplikasi native lama tanpa pembaruan bukanlah tujuannya
    • Aplikasi native perlu diperbarui agar membaca format Web Custom Formats
  • Web Custom Formats dapat digunakan di browser berbasis Chromium sejak 2022, tetapi browser lain belum mengimplementasikannya

Opsi unsanitized

  • Saat membaca clipboard dengan async Clipboard API, browser dapat menyanitasi data
    • Browser dapat menghapus tag script yang berpotensi berbahaya dari HTML
    • Browser dapat meng-encode ulang gambar PNG untuk menghindari serangan zip bomb
  • Opsi unsanitized pada read() memungkinkan permintaan data yang tidak disanitasi
  • Opsi ini saat ini hanya didukung di browser berbasis Chromium
    • Ditambahkan pada akhir 2023
    • Browser lain dapat mendukungnya di masa mendatang, tetapi ada umpan balik bahwa Safari tampaknya kecil kemungkinannya

Pilihan realistis

  • Saat ini belum ada cara menulis tipe clipboard kustom yang bekerja baik di semua browser
  • Cara seperti Figma, yaitu memasukkan string base64 ke dalam representasi HTML, memang kasar, tetapi dapat meneruskan data kustom melalui clipboard dengan melewati berbagai batasan Clipboard API
  • Web Custom Formats menyediakan arah yang lebih aman dan praktis untuk memakai tipe kustom, tetapi perlu diimplementasikan di seluruh browser utama

1 komentar

 
GN⁺ 2024-09-02
Pendapat Hacker News
  • Artikel yang bagus. Saya jadi paham kenapa tempel dengan/tanpa format di Google Docs pada Firefox di Mac sering tidak berjalan dengan baik.
    Menarik bahwa Google memakai API yang sudah ditinggalkan untuk mencoba memasukkan format kontennya sendiri ke clipboard.

    • Suatu hari nanti mungkin kita harus secara resmi menyatakan Google Web sebagai fork dari web resmi.
  • Cara paling andal untuk mengirim data aplikasi kustom ke clipboard tampaknya adalah menyematkan data di dalam HTML, seperti Figma.
    Keuntungannya, ketika aplikasi penerima tidak mendukungnya, perilaku kegagalan pun bisa didefinisikan lewat pesan HTML.

  • WordPress juga menyentuh masalah ini. Jika memotong dan menempel beberapa paragraf dalam mode editor, hasilnya bisa rusak parah.
    Mungkin karena paragraf-paragraf itu sebenarnya berada di area DIV yang berbeda-beda, serta berada di bawah logika kontrol dan efek yang berbeda.
    Selain itu, mesin menaikkan sesuatu yang saya kira ASCII menjadi UTF-8 atau ISO Latin-1, lalu bertindak seperti Clippy: “Saya sudah membuat tanda kutipnya terlihat lebih bagus, nanti berterima kasihlah.” Padahal saya tidak pernah menginginkannya. Kalau saya menginginkan `this', saya tidak akan mengetik 'this'.

  • Dulu ketika masih mahasiswa, JavaScript bisa membaca clipboard tanpa persetujuan pengguna, dan saya membuat situs web bernama getpasted.
    Situs itu secara otomatis menempelkan isi clipboard dan mengunggahnya ke basis data publik, dan tentu saja ada orang yang tidak menyukainya.
    Meski begitu, sebagai proyek untuk menunjukkan bahwa clipboard bisa dibaca kapan saja, itu cukup baik.

    • Orang-orang akan terkejut saat mengetahui bahwa di Windows 11, secara default semua data clipboard dikirim ke server Microsoft melalui Clipboard History / Cloud Clipboard.
  • Sindre Sorhus, pembuat Pasteboard Manager[0], juga membuat pustaka shortcut Actions[1] serta beberapa aplikasi iPhone dan Mac. Saya tidak tahu apakah dia ada di HN.
    0. https://apps.apple.com/us/app/pasteboard-viewer/id1499215709

    1. https://apps.apple.com/us/app/actions/id1586435171
  • Kombinasi cakupan dan kedalamannya sangat bagus. Artikel ini membahas perbedaan antar sistem dan browser, sambil juga menjelaskan jebakan dan cara mengatasinya.

    • Saya sudah berusaha mengurangi detail berlebihan dan pembahasan sampingan dalam artikel. Artikelnya memang cukup panjang, tapi saya senang kali ini rasanya keseimbangannya pas.
  • Terkait pembahasan tentang mengambil data “privat” dari browser, webapp bank saya saat login menampilkan hostname saya, aluminium, dan meminta autentikasi dua faktor.
    Saya tidak tahu bagaimana mereka mengetahuinya. Saat mengakses dari ponsel, aplikasi itu juga bisa melihat SMS berisi kode autentikasi dua faktor dan menempelkannya otomatis. Lingkungannya Pixel+Chrome atau Linux+Chrome.

    • Kode autentikasi dua faktor kemungkinan besar adalah fitur Chrome. Safari juga menampilkan kode verifikasi SMS sebagai saran autofill, dan Chrome di Android sepertinya bisa langsung mengisinya otomatis.
      Hostname lebih sulit. Perlu dipastikan apakah Anda benar-benar tidak pernah memberikan informasi itu ke bank dalam bentuk seperti username. Saya juga penasaran bank mana itu.
  • Artikel ini menunjukkan dengan baik mengapa webapp tidak bisa menjadi sebagus aplikasi native.
    Webapp selalu berupa kode yang “tidak tepercaya”, sehingga akses ke resource mesin lokal dibatasi secara arbitrer dan artifisial.

    • Tidak memercayai secara default adalah sebuah fitur. Kecuali pengguna adalah profesional keamanan yang sangat terlatih dan selalu waspada, sulit bagi mereka untuk melindungi diri sendiri.
      Sekarang bukan lagi era awal internet ketika script kiddie merajalela, dan juga bukan ruang yang hanya dipakai para geek. Ini era ketika organisasi yang terkait pemerintah melakukan serangan ransomware terhadap infrastruktur penting untuk mendanai program nuklir. Akses sembarang ke resource mesin lokal adalah hal yang memungkinkan kejadian seperti itu.
      Webapp di browser modern secara alami berada dalam sandbox yang lebih kuat, tetapi di sistem operasi modern aplikasi native pun diperlakukan sebagai pihak yang tidak tepercaya. Saat menjalankan aplikasi baru, dialog konfirmasi akan muncul sebelum aplikasi itu mengakses sesuatu di luar direktori data aplikasi yang terisolasi.
  • Ngomong-ngomong, saya penasaran ekstensi Chrome apa yang memunculkan peringatan di MacBook itu.