Web Clipboard và cách lưu nhiều loại dữ liệu khác nhau
(alexharri.com)- Web clipboard có thể lưu dữ liệu đã sao chép dưới nhiều biểu diễn MIME cùng lúc, để mỗi ứng dụng chọn đúng định dạng mình cần khi dán
- Async Clipboard API bị giới hạn chủ yếu ở
text/plain,text/html,image/png, nên các kiểu tùy chỉnh nhưapplication/jsonsẽ bị từ chối ghi vì lý do bảo mật - Clipboard Events API cũ có thể xử lý chuỗi kiểu dữ liệu tùy ý, nhưng clipboard hệ thống thực tế chỉ thay đổi trong sự kiện người dùng đáng tin cậy
- Google Docs tạo sự kiện copy đáng tin cậy bằng
execCommand("copy"), còn Figma nhúng dữ liệu tùy chỉnh vào thuộc tính HTML base64 để xử lý sao chép giữa trình duyệt và ứng dụng native - Web Custom Formats mở ra kiểu tùy chỉnh cho async Clipboard API bằng tiền tố
"web ", nhưng cần trình duyệt dựa trên Chromium và ứng dụng native đã được cập nhật
Mô hình cơ bản của web clipboard
- Clipboard có thể lưu nhiều biểu diễn (representation) cho một thao tác sao chép, kèm theo kiểu MIME
text/plain: văn bản thuầntext/html: HTMLimage/png: ảnh PNG
- Google Docs đọc biểu diễn
text/htmlđể giữ nguyên định dạng như liên kết, cỡ chữ, màu sắc - Các ứng dụng chỉ cần văn bản gốc như VS Code thì chỉ cần đọc biểu diễn
text/plain
Giới hạn kiểu của async Clipboard API
navigator.clipboard.read()kiểm tratypescủa mục clipboard rồi đọc một biểu diễn cụ thể nhưgetType("text/html")navigator.clipboard.write()có thể ghi nhiềuBlobcùng lúc trong mộtClipboardItem, với khóa là kiểu MIME- Nếu thử ghi
application/jsonbằngClipboardItem, sẽ phát sinh ngoại lệType application/json not supported on write - Đặc tả Clipboard của W3C yêu cầu
writetừ chối các kiểu khác ngoàitext/plain,text/html,image/png application/jsontừng nằm trong danh sách kiểu dữ liệu bắt buộc từ năm 2012 đến 2021, nhưng đã bị xóa trong w3c/clipboard-apis#155- Trước thay đổi này, danh sách kiểu dữ liệu bắt buộc gồm 16 kiểu cho đọc và 8 kiểu cho ghi
- Danh sách bị rút gọn vì các trình duyệt không hỗ trợ nhiều kiểu bắt buộc do lo ngại bảo mật
- Đặc tả cảnh báo rằng script không đáng tin cậy có thể đưa vào clipboard dữ liệu gây khai thác lỗ hổng phần mềm cục bộ, nên các kiểu được phép bị giới hạn
Clipboard Events API và isTrusted
ClipboardEventphát sinh trong các sự kiện copy, cut, paste và cung cấp đối tượngDataTransferqua thuộc tínhclipboardData- Trong sự kiện copy, có thể ghi dữ liệu bằng
e.clipboardData.setData("text/plain", "..."),setData("text/html", "...") - Trong sự kiện paste, có thể đọc biểu diễn HTML bằng
e.clipboardData.getData("text/html") clipboardData.setData("application/json", json)không ném ngoại lệ, và trong handler paste cũng có thể thấy kiểuapplication/jsoncùng chuỗi JSONclipboardDatalà giao diện cũ hơn rất nhiều so với async Clipboard API- Async Clipboard API được thêm vào đặc tả năm 2017
clipboardData,setData,getDatađã xuất hiện trong bản nháp W3C từ năm 2006- Bản nháp đó phản ánh đáng kể các tính năng đã được triển khai trong Internet Explorer
- Internet Explorer 4 phát hành năm 1997, nên giao diện
clipboardDatacó thể xem là đã tồn tại ít nhất 26 năm tại thời điểm bài viết được viết
- Từ đặc tả năm 2011 mới có kiểu MIME, nhưng đặc tính cho phép truyền bất kỳ chuỗi nào làm đối số kiểu cho
setData()vẫn được giữ đến nay Event.isTrustedlà thuộc tính chỉ đọc, chỉ có giá trịtruekhi sự kiện được tạo bởi user agent- Sự kiện copy khi người dùng nhấn Command+C có
isTrustedlàtrue - Sự kiện tổng hợp tạo bằng
dispatchEvent()cóisTrustedlàfalse
- Sự kiện copy khi người dùng nhấn Command+C có
- Các sự kiện copy/cut tổng hợp không thể sửa clipboard hệ thống, còn sự kiện paste tổng hợp không thể truy cập dữ liệu clipboard hệ thống thực
- Vì vậy, Clipboard Events API có thể xử lý kiểu tùy ý, nhưng tương tác với clipboard thực chỉ giới hạn trong handler copy/paste do user agent tạo ra
Ghi kiểu tùy chỉnh từ nút Copy
- Nút Copy trong menu chuột phải của Google Docs ghi ba biểu diễn vào clipboard
text/plaintext/htmlapplication/x-vnd.google-docs-document-slice-clip+wrapped
- Biểu diễn thứ ba là một kiểu tùy chỉnh chứa dữ liệu JSON, không thể ghi chỉ bằng async Clipboard API
- Khi nhấn nút Copy, Google Docs gọi
document.execCommand("copy") execCommand("copy")có thể tạo ra sự kiện copy đáng tin cậy theo chương trình, như thể người dùng đã thực hiện lệnh sao chép- Trong Safari,
execCommand("copy")cần có vùng chọn đang hoạt động để phát sinh sự kiện copy- Có thể tạo vùng chọn bằng cách thêm một input không rỗng vào DOM, chọn nó, gọi
execCommand("copy"), rồi xóa đi
- Có thể tạo vùng chọn bằng cách thêm một input không rỗng vào DOM, chọn nó, gọi
Khác biệt trình duyệt·OS của nút Paste
- Nút Paste của Google Docs yêu cầu cài extension trên macOS, nhưng lại hoạt động trên laptop Windows
- Có thể dùng
document.queryCommandSupported("paste")để kiểm tra hỗ trợ lệnh paste- Trên macOS, Chrome và Firefox trả về
false, Safari trả vềtrue - Trên Windows, Chrome và Edge trả về
true, Firefox trả vềfalse
- Trên macOS, Chrome và Firefox trả về
- Safari hiển thị prompt xác nhận thao tác paste để cho người dùng biết rõ website đang đọc clipboard
- Chưa xác định được vì sao Chrome cho phép
execCommand("paste")trên Windows nhưng không cho trên macOS - Google Docs không fallback sang async Clipboard API khi không dùng được
execCommand("paste")- Async Clipboard API không đọc được biểu diễn
application/x-vnd.google-[...] - Biểu diễn HTML có chứa ID nội bộ
- Async Clipboard API không đọc được biểu diễn
Cơ chế copy·paste dựa trên HTML của Figma
- Nút Copy của Figma ghi hai biểu diễn vào clipboard
text/plaintext/html
- Biểu diễn HTML chứa hai thẻ
spanrỗng, lần lượt có thuộc tínhdata-metadata,data-buffer data-metadatalà chuỗi JSON được mã hóa base64; sau khi giải mã có thể thấy các giá trị nhưfileKey,pasteID,dataTypedata-bufferdài khoảng 26.000 ký tự ngay cả khi chỉ sao chép một frame rỗng, và có vẻ tăng tuyến tính theo lượng nội dung đã sao chép- Khi giải mã base64
data-buffer, sẽ thu được dữ liệu nhị phân bắt đầu bằngfig-kiwi, tức Kiwi message format- Kiwi là định dạng do Evan Wallace, đồng sáng lập và cựu CTO của Figma, tạo ra
- Nó được dùng để mã hóa tệp
.fig
- Có thể dùng
.figfile parser do Evan Wallace công bố để chuyểndata-bufferthành tệp.figvà kiểm tra - Figma tạo một tệp Figma nhỏ, mã hóa nó bằng base64, rồi đặt vào thuộc tính
data-buffercủa mộtspanHTML rỗng để lưu vào clipboard
Vì sao dùng biểu diễn HTML
- Windows, macOS và Linux cung cấp các định dạng OS khác nhau để ghi dữ liệu vào clipboard
- Windows cung cấp
CF_HTMLcho HTML - macOS cung cấp
NSPasteboard.PasteboardType.html
- Windows cung cấp
- Văn bản thuần, HTML và ảnh PNG có thể được ánh xạ sang các định dạng chuẩn trên nhiều OS
- Các kiểu tùy ý như
application/foo-barkhông khớp tốt với định dạng chung của clipboard OS - Trình duyệt không ghi các biểu diễn tùy chỉnh đó vào định dạng clipboard chung của OS, mà lưu chúng trong định dạng clipboard tùy chỉnh riêng của từng trình duyệt
- Kết quả là kiểu tùy chỉnh có thể copy·paste giữa các tab trong cùng trình duyệt
- Nhưng không thể copy·paste giữa các ứng dụng khác nhau
- Khi dùng
text/html, dữ liệu có thể được ánh xạ sang định dạng clipboard chung của OS, nên ứng dụng khác dễ đọc hơn - Figma dùng
text/htmlđể copy·paste phần tử giữafigma.comtrong trình duyệt và ứng dụng Figma native
Cách mỗi trình duyệt lưu dữ liệu tùy chỉnh
- Trên macOS, nếu ghi
text/plain,text/html,application/json,foo bar baztrong sự kiện copy rồi kiểm tra bằng Pasteboard Viewer, có thể thấy mỗi trình duyệt lưu khác nhau - Chrome thêm bốn mục vào Pasteboard
public.html: biểu diễn HTMLpublic.utf8-plain-text: biểu diễn văn bản thuầnorg.chromium.web-custom-data: biểu diễn tùy chỉnhorg.chromium.source-url: URL trang web nơi thao tác copy diễn ra
- Firefox cũng tạo
public.html,public.utf8-plain-text, nhưng ghi dữ liệu tùy chỉnh vàoorg.mozilla.custom-clipdata- Khác với Chrome, Firefox không lưu source URL
- Safari cũng tạo
public.html,public.utf8-plain-text, và ghi dữ liệu tùy chỉnh vàocom.apple.WebKit.custom-pasteboard-data- Trong mục này, nó còn lưu cả danh sách biểu diễn đầy đủ gồm văn bản thuần, HTML và source URL
- Safari chỉ cho phép copy·paste kiểu tùy chỉnh giữa các tab trình duyệt khi domain của source URL giống nhau
- Trên Chrome và Firefox không thấy giới hạn này
Raw Clipboard Access và Web Custom Formats
- Raw Clipboard Access là API được đề xuất năm 2019 nhằm cung cấp cho ứng dụng web quyền truy cập đọc·ghi thô vào clipboard OS native
- Raw Clipboard Access giải quyết vấn đề ứng dụng web bị giới hạn ở một số ít định dạng, khiến Figma và Photopea khó tương tác với phần lớn định dạng ảnh
- Đề xuất này không tiến thêm do lo ngại bảo mật như thực thi mã từ xa trong ứng dụng native
- Web Custom Formats là đề xuất cho phép ghi kiểu tùy chỉnh trong async Clipboard API, và Chromium đã triển khai vào năm 2022
- Web Custom Formats dùng tiền tố
"web "trước kiểu MIME- Ví dụ:
web application/json - Khi đọc cũng dùng
read()của async Clipboard API vàgetType("web application/json")
- Ví dụ:
- Web Custom Formats ghi hai loại dữ liệu vào clipboard OS native
- Ánh xạ giữa kiểu dữ liệu và tên mục clipboard
- Mục clipboard ứng với từng kiểu dữ liệu
- Trên macOS, ánh xạ được lưu trong
org.w3.web-custom-format.map- Ví dụ:
"application/json": "org.w3.web-custom-format.type-0"
- Ví dụ:
- Mục
org.w3.web-custom-format.type-[index]chứa dữ liệu thô của Blob - Cách này ngăn ứng dụng web ghi trực tiếp dữ liệu thô vào bất kỳ định dạng clipboard OS nào chúng muốn, nhờ đó tránh được vấn đề bảo mật của Raw Clipboard Access
- Đổi lại, mục tiêu không phải là tương tác ngay với ứng dụng native cũ mà không cần cập nhật
- Ứng dụng native phải được cập nhật để đọc định dạng Web Custom Formats
- Web Custom Formats đã dùng được trên các trình duyệt dựa trên Chromium từ năm 2022, nhưng các trình duyệt khác vẫn chưa triển khai
Tùy chọn unsanitized
- Khi đọc clipboard bằng async Clipboard API, trình duyệt có thể làm sạch dữ liệu
- Có thể xóa các thẻ script nguy hiểm trong HTML
- Có thể mã hóa lại ảnh PNG để tránh tấn công zip bomb
- Tùy chọn
unsanitizedcủaread()cho phép yêu cầu dữ liệu chưa qua làm sạch - Tùy chọn này hiện chỉ được hỗ trợ trong các trình duyệt dựa trên Chromium
- Được thêm vào cuối năm 2023
- Các trình duyệt khác có thể hỗ trợ trong tương lai, nhưng có phản hồi cho thấy Safari khó có khả năng hỗ trợ
Lựa chọn thực tế
- Hiện vẫn chưa có cách ghi kiểu clipboard tùy chỉnh nào hoạt động tốt trên mọi trình duyệt
- Cách nhúng chuỗi base64 vào biểu diễn HTML như Figma tuy thô nhưng có thể vượt qua nhiều giới hạn của Clipboard API để truyền dữ liệu tùy chỉnh qua clipboard
- Web Custom Formats đưa ra hướng đi an toàn và thực tế hơn cho việc dùng kiểu tùy chỉnh, nhưng cần được triển khai rộng rãi trên các trình duyệt lớn
1 bình luận
Ý kiến trên Hacker News
Bài viết hay. Giờ thì tôi hiểu vì sao dán có/không kèm định dạng trong Google Docs trên Firefox của Mac hoạt động không ổn
Điều thú vị là Google đang dùng một API đã bị loại bỏ để cố đưa định dạng nội dung riêng của họ vào clipboard
Cách đáng tin cậy nhất để đưa dữ liệu ứng dụng tùy chỉnh vào clipboard có vẻ là nhúng dữ liệu vào HTML, giống như Figma
Ưu điểm là khi ứng dụng nhận không hỗ trợ, bạn còn có thể định nghĩa cả hành vi thất bại bằng một thông điệp HTML
WordPress cũng đụng đến vấn đề này. Nếu cắt rồi dán nhiều đoạn văn trong chế độ trình soạn thảo, mọi thứ có thể hỏng nặng
Có lẽ vì các đoạn văn thực ra nằm trong những vùng DIV khác nhau, dưới các logic điều khiển và hiệu ứng khác nhau
Ngoài ra, máy còn tự nâng thứ tôi nghĩ là ASCII lên UTF-8 hoặc ISO Latin-1, rồi hành xử kiểu Clippy như “tôi đã đổi dấu ngoặc kép cho đẹp hơn, sau này nhớ cảm ơn nhé”, trong khi tôi chẳng hề muốn. Nếu tôi muốn `this' thì tôi đã không gõ 'this'
Hồi còn là sinh viên, JavaScript từng có thể đọc clipboard mà không cần người dùng đồng ý, và tôi đã làm một website tên là getpasted
Đó là trang tự động dán nội dung clipboard rồi đưa lên cơ sở dữ liệu công khai, và tất nhiên cũng có người ghét nó
Dù vậy, xét như một dự án để cho thấy việc có thể đọc clipboard bất cứ lúc nào là hoàn toàn có thật thì cũng ổn
Sindre Sorhus, tác giả của Pasteboard Manager[0], cũng là người tạo ra thư viện shortcut Actions[1] và nhiều ứng dụng iPhone/Mac khác. Không rõ anh ấy có ở trên HN không
0. https://apps.apple.com/us/app/pasteboard-viewer/id1499215709
https://www.npmjs.com/~sindresorhus
Sự kết hợp giữa phạm vi và chiều sâu đặc biệt tốt. Bài viết đề cập cả khác biệt theo hệ thống/trình duyệt, đồng thời giải thích cả cạm bẫy và cách đi vòng
Liên quan đến chuyện lấy dữ liệu “riêng tư” trong trình duyệt, webapp ngân hàng của tôi khi đăng nhập đã hiển thị hostname của tôi là aluminium và yêu cầu xác thực hai bước
Tôi không biết họ biết bằng cách nào. Khi truy cập trên di động, nó còn có thể xem SMS chứa mã xác thực hai bước và tự động dán vào. Đó là môi trường Pixel+Chrome hoặc Linux+Chrome
Hostname thì khó hơn. Cần kiểm tra xem bạn có thực sự chưa từng cung cấp thông tin đó cho ngân hàng dưới dạng như tên người dùng hay không. Tôi cũng tò mò đó là ngân hàng nào
Bài này cho thấy rõ vì sao webapp không thể tốt như ứng dụng native
Webapp luôn là mã “không đáng tin cậy”, nên quyền truy cập tài nguyên của máy cục bộ bị hạn chế một cách tùy ý và nhân tạo
Giờ không còn là thời Internet sơ khai khi script kiddie hoành hành nữa, cũng không phải là không gian chỉ dành cho dân geek. Đây là thời đại mà các tổ chức liên hệ với chính phủ tấn công ransomware vào hạ tầng trọng yếu để tài trợ cho chương trình hạt nhân. Quyền truy cập tùy ý vào tài nguyên máy cục bộ chính là thứ khiến những việc như vậy khả thi
Webapp trong các trình duyệt hiện đại tự nhiên nằm trong sandbox mạnh hơn, nhưng trên các hệ điều hành hiện đại, ứng dụng native cũng bị xem là đối tượng không đáng tin. Khi chạy một ứng dụng mới, hộp thoại xác nhận sẽ xuất hiện trước khi nó truy cập ra ngoài thư mục dữ liệu ứng dụng đã được cách ly
Nhưng tôi tò mò tiện ích mở rộng Chrome nào đã làm hiện cảnh báo trên MacBook