เว็บคลิปบอร์ดกับวิธีจัดเก็บข้อมูลหลายประเภท
(alexharri.com)- เว็บคลิปบอร์ดจัดเก็บข้อมูลที่คัดลอกหนึ่งครั้งไว้พร้อมกันใน การแทนค่า MIME หลายแบบ ทำให้แต่ละแอปเลือกวางเฉพาะรูปแบบที่ต้องการได้
- async Clipboard API ถูกจำกัดไว้หลัก ๆ ที่
text/plain,text/html,image/pngทำให้ประเภทกำหนดเองอย่างapplication/jsonถูก ปฏิเสธการเขียนด้วยเหตุผลด้านความปลอดภัย - Clipboard Events API รุ่นเก่าสามารถจัดการสตริงประเภทใดก็ได้ แต่คลิปบอร์ดของระบบจริงจะเปลี่ยนได้เฉพาะภายใน เหตุการณ์จากผู้ใช้ที่เชื่อถือได้ เท่านั้น
- Google Docs สร้างเหตุการณ์ copy ที่เชื่อถือได้ด้วย
execCommand("copy")ส่วน Figma ใส่ข้อมูลกำหนดเองไว้ใน แอตทริบิวต์ HTML แบบ base64 เพื่อจัดการการคัดลอกระหว่างเบราว์เซอร์กับแอปเนทีฟ - Web Custom Formats เปิดให้ใช้ประเภทกำหนดเองใน async Clipboard API ด้วยพรีฟิกซ์
"web "แต่ต้องใช้เบราว์เซอร์ฐาน Chromium และแอปเนทีฟที่อัปเดตแล้ว
โมเดลพื้นฐานของเว็บคลิปบอร์ด
- คลิปบอร์ดสามารถจัดเก็บ การแทนค่า (representation) หลายแบบพร้อม MIME type สำหรับการคัดลอกหนึ่งครั้งได้
text/plain: ข้อความธรรมดาtext/html: HTMLimage/png: รูปภาพ PNG
- Google Docs อ่านการแทนค่า
text/htmlเพื่อรักษารูปแบบอย่างลิงก์ ขนาดตัวอักษร และสี - แอปที่ต้องการเฉพาะข้อความต้นฉบับอย่าง VS Code อ่านเพียงการแทนค่า
text/plainก็พอ
ข้อจำกัดด้านประเภทของ async Clipboard API
navigator.clipboard.read()ตรวจสอบtypesของรายการในคลิปบอร์ด แล้วอ่านการแทนค่าที่ต้องการ เช่นgetType("text/html")navigator.clipboard.write()สามารถใส่Blobหลายตัวไว้ในClipboardItemที่ใช้ MIME type เป็นคีย์ แล้วเขียนได้ในครั้งเดียว- หากพยายามเขียน
application/jsonด้วยClipboardItemจะเกิดข้อยกเว้นType application/json not supported on write - สเปก W3C Clipboard กำหนดให้ปฏิเสธประเภทที่ไม่ใช่
text/plain,text/html,image/pngในการwrite application/jsonเคยอยู่ในรายการประเภทข้อมูลบังคับตั้งแต่ปี 2012 ถึง 2021 แต่ถูกนำออกใน w3c/clipboard-apis#155- ก่อนเปลี่ยนแปลง มีประเภทข้อมูลบังคับสำหรับการอ่าน 16 ประเภท และสำหรับการเขียน 8 ประเภท
- รายการถูกลดลงเพราะเบราว์เซอร์จำนวนมากไม่รองรับประเภทบังคับหลายประเภทด้วยความกังวลด้านความปลอดภัย
- สเปกเตือนว่า สคริปต์ที่ไม่น่าเชื่อถืออาจใส่ข้อมูลที่กระตุ้นช่องโหว่ของซอฟต์แวร์ในเครื่องลงในคลิปบอร์ดได้ จึง จำกัดประเภทที่อนุญาต
Clipboard Events API และ isTrusted
ClipboardEventเกิดขึ้นในเหตุการณ์ copy, cut, paste และให้วัตถุDataTransferผ่านพร็อพเพอร์ตีclipboardData- ในเหตุการณ์ copy สามารถเขียนข้อมูลด้วย
e.clipboardData.setData("text/plain", "..."),setData("text/html", "...") - ในเหตุการณ์ paste สามารถอ่านการแทนค่า HTML ด้วย
e.clipboardData.getData("text/html") clipboardData.setData("application/json", json)ไม่โยนข้อยกเว้น และใน paste handler ก็สามารถเห็นประเภทapplication/jsonกับสตริง JSON ได้clipboardDataเป็นอินเทอร์เฟซที่เก่ากว่า async Clipboard API มาก- async Clipboard API ถูกเพิ่มเข้าในสเปกในปี 2017
clipboardData,setData,getDataปรากฏอยู่แล้วในร่าง W3C ปี 2006- ร่างดังกล่าวสะท้อนฟีเจอร์ที่นำไปใช้ใน Internet Explorer เป็นอย่างมาก
- เนื่องจาก Internet Explorer 4 เปิดตัวในปี 1997 จึงมองได้ว่าอินเทอร์เฟซ
clipboardDataมีอายุอย่างน้อย 26 ปี ณ เวลาที่เขียนบทความ
- MIME type ถูกใส่เข้ามาตั้งแต่สเปกปี 2011 แต่คุณสมบัติที่อาร์กิวเมนต์ประเภทของ
setData()สามารถใช้สตริงใดก็ได้ยังคงอยู่จนถึงปัจจุบัน Event.isTrustedเป็นพร็อพเพอร์ตีแบบอ่านอย่างเดียวที่จะเป็นtrueเฉพาะเมื่อเหตุการณ์ถูกสร้างโดย user agent- เหตุการณ์ copy ที่ผู้ใช้กด Command+C จะมี
isTrustedเป็นtrue - เหตุการณ์สังเคราะห์ที่สร้างด้วย
dispatchEvent()จะมีisTrustedเป็นfalse
- เหตุการณ์ copy ที่ผู้ใช้กด Command+C จะมี
- เหตุการณ์ copy/cut สังเคราะห์ไม่สามารถแก้ไขคลิปบอร์ดของระบบได้ และเหตุการณ์ paste สังเคราะห์ไม่สามารถเข้าถึงข้อมูลจริงในคลิปบอร์ดของระบบได้
- ดังนั้น Clipboard Events API จึงจัดการ ประเภทใดก็ได้ แต่การโต้ตอบกับคลิปบอร์ดจริงถูกจำกัดไว้ใน handler ของเหตุการณ์ copy/paste ที่ user agent สร้างขึ้น
การเขียนประเภทกำหนดเองจากปุ่ม Copy
- ปุ่ม Copy ในเมนูคลิกขวาของ Google Docs เขียนการแทนค่าสามแบบลงคลิปบอร์ด
text/plaintext/htmlapplication/x-vnd.google-docs-document-slice-clip+wrapped
- การแทนค่าที่สามเป็นประเภทกำหนดเองที่บรรจุข้อมูล JSON และไม่สามารถเขียนได้ด้วย async Clipboard API เพียงอย่างเดียว
- Google Docs เรียก
document.execCommand("copy")เมื่อคลิกปุ่ม Copy execCommand("copy")สามารถสร้าง เหตุการณ์ copy ที่เชื่อถือได้ แบบโปรแกรมได้ เหมือนผู้ใช้สั่งคัดลอกเอง- ใน Safari หากต้องการให้
execCommand("copy")สร้างเหตุการณ์ copy จำเป็นต้องมี selection ที่ใช้งานอยู่- สามารถสร้าง selection ได้ด้วยการเพิ่ม input ที่ไม่ว่างลงใน DOM เลือกข้อความ เรียก
execCommand("copy")แล้วลบออก
- สามารถสร้าง selection ได้ด้วยการเพิ่ม input ที่ไม่ว่างลงใน DOM เลือกข้อความ เรียก
ความแตกต่างของเบราว์เซอร์และ OS สำหรับปุ่ม Paste
- ปุ่ม Paste ของ Google Docs เคยกำหนดให้ติดตั้งส่วนขยายบน macOS แต่ทำงานได้บนแล็ปท็อป Windows
- สามารถตรวจสอบการรองรับคำสั่ง paste ได้ด้วย
document.queryCommandSupported("paste")- บน macOS Chrome และ Firefox เป็น
false, Safari เป็นtrue - บน Windows Chrome และ Edge เป็น
true, Firefox เป็นfalse
- บน macOS Chrome และ Firefox เป็น
- Safari แสดงพรอมป์ยืนยันการวาง ทำให้เห็นชัดว่าเว็บไซต์กำลังอ่านคลิปบอร์ด
- ยังไม่ทราบเหตุผลที่ Chrome อนุญาต
execCommand("paste")บน Windows แต่ไม่อนุญาตบน macOS - Google Docs ไม่ fallback ไปใช้ async Clipboard API เมื่อใช้
execCommand("paste")ไม่ได้- async Clipboard API ไม่สามารถอ่านการแทนค่า
application/x-vnd.google-[...]ได้ - การแทนค่า HTML มี ID ภายในอยู่
- async Clipboard API ไม่สามารถอ่านการแทนค่า
การคัดลอกและวางของ Figma ที่อิง HTML
- ปุ่ม Copy ของ Figma เขียนการแทนค่าสองแบบลงคลิปบอร์ดคือ
text/plainและtext/html - การแทนค่า HTML มี
spanว่างสองตัว โดยแต่ละตัวมีแอตทริบิวต์data-metadata,data-buffer data-metadataเป็นสตริง JSON ที่เข้ารหัส base64 และเมื่อถอดรหัสจะได้ค่าต่าง ๆ เช่นfileKey,pasteID,dataTypedata-bufferมีความยาวประมาณ 26,000 ตัวอักษรแม้คัดลอกแค่เฟรมว่าง และดูเหมือนความยาวจะเพิ่มขึ้นเชิงเส้นตามปริมาณคอนเทนต์ที่คัดลอก- เมื่อถอดรหัส base64 ของ
data-bufferจะได้ข้อมูลไบนารีที่ขึ้นต้นด้วยfig-kiwiซึ่งเป็น Kiwi message format- Kiwi เป็นฟอร์แมตที่สร้างโดย Evan Wallace ผู้ร่วมก่อตั้ง Figma และอดีต CTO
- ใช้ในการเข้ารหัสไฟล์
.fig
- สามารถใช้ parser ไฟล์
.figที่ Evan Wallace เผยแพร่ เพื่อแปลงdata-bufferเป็นไฟล์.figแล้วตรวจสอบได้ - Figma สร้างไฟล์ Figma ขนาดเล็ก เข้ารหัสเป็น base64 แล้วใส่ไว้ในแอตทริบิวต์
data-bufferของspanHTML ว่าง เพื่อบันทึกลงคลิปบอร์ด
เหตุผลที่ใช้การแทนค่า HTML
- Windows, macOS, Linux มีฟอร์แมต OS ที่ต่างกันสำหรับเขียนข้อมูลลงคลิปบอร์ด
- Windows มี
CF_HTMLสำหรับ HTML - macOS มี
NSPasteboard.PasteboardType.html
- Windows มี
- ข้อความธรรมดา, HTML, รูปภาพ PNG สามารถแมปเป็นฟอร์แมตมาตรฐานในหลาย OS ได้
- ประเภทใดก็ได้อย่าง
application/foo-barไม่ค่อยเข้ากับฟอร์แมตกลางของคลิปบอร์ด OS - เบราว์เซอร์ไม่เขียนการแทนค่ากำหนดเองเหล่านี้ลงในฟอร์แมตคลิปบอร์ดกลางของ OS แต่เก็บไว้ในฟอร์แมตคลิปบอร์ดกำหนดเองของแต่ละเบราว์เซอร์
- ผลคือประเภทกำหนดเองสามารถคัดลอกและวางระหว่างแท็บของเบราว์เซอร์ได้
- แต่คัดลอกและวางระหว่างแอปพลิเคชันไม่ได้
- การใช้
text/htmlจะถูกแมปเป็นฟอร์แมตคลิปบอร์ดกลางของ OS ทำให้แอปพลิเคชันอื่นอ่านได้ง่าย - Figma ใช้
text/htmlเพื่อให้คัดลอกและวางองค์ประกอบระหว่างfigma.comในเบราว์เซอร์กับแอป Figma แบบเนทีฟได้
วิธีจัดเก็บข้อมูลกำหนดเองในแต่ละเบราว์เซอร์
- บน macOS หากเขียน
text/plain,text/html,application/json,foo bar bazภายในเหตุการณ์ copy แล้วตรวจสอบด้วย Pasteboard Viewer จะเห็นว่าแต่ละเบราว์เซอร์จัดเก็บไม่เหมือนกัน - Chrome เพิ่มรายการสี่รายการใน Pasteboard
public.html: การแทนค่า HTMLpublic.utf8-plain-text: การแทนค่าข้อความธรรมดาorg.chromium.web-custom-data: การแทนค่ากำหนดเองorg.chromium.source-url: URL ของหน้าเว็บที่ทำการคัดลอก
- Firefox ก็สร้าง
public.html,public.utf8-plain-textแต่เขียนข้อมูลกำหนดเองไว้ในorg.mozilla.custom-clipdata- ต่างจาก Chrome ตรงที่ไม่บันทึก source URL
- Safari ก็สร้าง
public.html,public.utf8-plain-textและเขียนข้อมูลกำหนดเองไว้ในcom.apple.WebKit.custom-pasteboard-data- ในรายการนี้ยังเก็บรายการการแทนค่าทั้งหมดรวมถึงข้อความธรรมดาและ HTML ตลอดจน source URL ด้วย
- Safari อนุญาตให้คัดลอกและวางประเภทกำหนดเองระหว่างแท็บเบราว์เซอร์เฉพาะเมื่อโดเมนของ source URL เหมือนกัน
- ใน Chrome และ Firefox ไม่พบข้อจำกัดแบบเดียวกัน
Raw Clipboard Access และ Web Custom Formats
- Raw Clipboard Access เป็น API ที่เสนอในปี 2019 โดยตั้งใจให้เว็บแอปพลิเคชันเข้าถึงการอ่านและเขียนแบบดิบของคลิปบอร์ด OS เนทีฟ
- Raw Clipboard Access แก้ปัญหาที่เว็บแอปพลิเคชันถูกจำกัดไว้กับฟอร์แมตไม่กี่ชนิด ทำให้ Figma และ Photopea ทำงานร่วมกับฟอร์แมตรูปภาพส่วนใหญ่ได้ยาก
- ข้อเสนอนี้ไม่ได้เดินหน้าต่อ เนื่องจาก ความกังวลด้านความปลอดภัย เช่น การรันโค้ดจากระยะไกลในแอปพลิเคชันเนทีฟ
- Web Custom Formats เป็นข้อเสนอสำหรับใช้ประเภทกำหนดเองใน async Clipboard API และ Chromium นำไปใช้ในปี 2022
- Web Custom Formats ใช้โดยเติม
"web "นำหน้า MIME type- ตัวอย่าง:
web application/json - ตอนอ่านก็ใช้
read()ของ async Clipboard API และgetType("web application/json")
- ตัวอย่าง:
- Web Custom Formats เขียนข้อมูลสองประเภทลงในคลิปบอร์ด OS เนทีฟ
- การแมประหว่าง data type กับชื่อรายการคลิปบอร์ด
- รายการคลิปบอร์ดของแต่ละ data type
- บน macOS การแมปถูกเก็บไว้ใน
org.w3.web-custom-format.map- ตัวอย่าง:
"application/json": "org.w3.web-custom-format.type-0"
- ตัวอย่าง:
- รายการ
org.w3.web-custom-format.type-[index]มี ข้อมูลดิบ ของ Blob อยู่ - วิธีนี้หลีกเลี่ยงปัญหาความปลอดภัยของ Raw Clipboard Access โดยไม่ให้เว็บแอปพลิเคชันเขียนข้อมูลดิบโดยตรงลงในฟอร์แมตคลิปบอร์ด OS ที่ต้องการ
- แต่เป้าหมายไม่ใช่การทำงานร่วมกับแอปพลิเคชันเนทีฟแบบ legacy โดยไม่ต้องอัปเดต
- แอปพลิเคชันเนทีฟต้องอัปเดตให้อ่านฟอร์แมต Web Custom Formats ได้
- Web Custom Formats ใช้ได้ในเบราว์เซอร์ฐาน Chromium ตั้งแต่ปี 2022 แต่เบราว์เซอร์อื่นยังไม่ได้ implement
ตัวเลือก unsanitized
- เมื่ออ่านคลิปบอร์ดด้วย async Clipboard API เบราว์เซอร์อาจ sanitize ข้อมูลได้
- อาจลบแท็ก script ที่อาจเป็นอันตรายออกจาก HTML
- อาจเข้ารหัสรูปภาพ PNG ใหม่เพื่อหลีกเลี่ยงการโจมตีแบบ zip bomb
- ตัวเลือก
unsanitizedของread()ทำให้สามารถร้องขอข้อมูลที่ไม่ถูก sanitize ได้ - ปัจจุบันตัวเลือกนี้รองรับเฉพาะเบราว์เซอร์ฐาน Chromium
- เพิ่มเข้ามาเมื่อปลายปี 2023
- เบราว์เซอร์อื่นอาจรองรับในอนาคต แต่มีฟีดแบ็กว่า Safari ดูมีความเป็นไปได้น้อย
ทางเลือกที่เป็นจริงในตอนนี้
- ตอนนี้ยังไม่มีวิธีเขียนประเภทคลิปบอร์ดกำหนดเองที่ทำงานได้ดีในทุกเบราว์เซอร์
- วิธีใส่สตริง base64 ไว้ในการแทนค่า HTML แบบ Figma นั้นค่อนข้างหยาบ แต่สามารถอ้อมข้อจำกัดหลายอย่างของ Clipboard API เพื่อส่งข้อมูลกำหนดเองผ่านคลิปบอร์ดได้
- Web Custom Formats ชี้แนวทางให้ใช้ประเภทกำหนดเองได้อย่างปลอดภัยและใช้งานได้จริงมากขึ้น แต่ยังต้องมีการ implement ครอบคลุมเบราว์เซอร์หลัก ๆ
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
เป็นบทความที่ดี เข้าใจแล้วว่าทำไมการ วางแบบคง/ไม่คงรูปแบบ ใน Google Docs บน Firefox ของ Mac ถึงทำงานได้ไม่ค่อยดี
น่าสนใจที่ Google ใช้ API ที่เลิกใช้แล้ว เพื่อพยายามใส่รูปแบบเนื้อหาของตัวเองลงในคลิปบอร์ด
วิธีที่น่าเชื่อถือที่สุดในการส่งข้อมูลแอปแบบกำหนดเองไปยังคลิปบอร์ด ดูเหมือนจะเป็น การฝังข้อมูลไว้ใน HTML แบบที่ Figma ทำ
ข้อดีอีกอย่างคือ เมื่อแอปที่รับไม่รองรับ ก็ยังสามารถกำหนดพฤติกรรมตอนล้มเหลวเป็นข้อความ HTML ได้
WordPress ก็แตะปัญหานี้เหมือนกัน ถ้าตัดและวางหลายย่อหน้าในโหมดตัวแก้ไข อาจพังหนักได้
น่าจะเพราะย่อหน้าเหล่านั้นจริง ๆ แล้วเป็นพื้นที่ DIV คนละส่วนกัน และอยู่ภายใต้ลอจิกควบคุมกับเอฟเฟกต์ที่ต่างกัน
อีกอย่างคือเครื่องมักยกระดับสิ่งที่ผมคิดว่าเป็น ASCII ให้เป็น UTF-8 หรือ ISO Latin-1 แล้วทำพฤติกรรมแบบ Clippy ว่า “ฉันเปลี่ยนเครื่องหมายอัญประกาศให้ดูดีขึ้นแล้ว ไว้ค่อยขอบคุณฉันทีหลังนะ” ซึ่งผมไม่เคยต้องการ ถ้าผมอยากได้ `this' ผมก็คงไม่พิมพ์ 'this' หรอก
สมัยก่อนตอนเป็นนักเรียน JavaScript ยังอ่านคลิปบอร์ดได้โดยไม่ต้องขอความยินยอมจากผู้ใช้ และผมเคยทำเว็บไซต์ชื่อ getpasted
มันเป็นเว็บที่วางเนื้อหาจากคลิปบอร์ดโดยอัตโนมัติแล้วอัปโหลดไปยังฐานข้อมูลสาธารณะ แน่นอนว่ามีคนไม่ชอบ
แต่ในฐานะโปรเจกต์ที่บอกให้รู้ว่าคลิปบอร์ดอ่านได้ตลอดเวลา ก็ถือว่าใช้ได้
Sindre Sorhus ผู้สร้าง Pasteboard Manager[0] ยังเป็นคนทำไลบรารี Shortcuts ชื่อ Actions[1] และแอป iPhone/Mac หลายตัวด้วย ไม่รู้ว่าเขาอยู่ใน HN หรือเปล่า
0. https://apps.apple.com/us/app/pasteboard-viewer/id1499215709
https://www.npmjs.com/~sindresorhus
การผสมกันของขอบเขตและความลึกดีเป็นพิเศษ ครอบคลุมถึงความแตกต่างตามระบบและเบราว์เซอร์ ขณะเดียวกันก็อธิบายทั้ง กับดักและวิธีเลี่ยง ไปด้วย
เกี่ยวกับเรื่องการดึงข้อมูล “ส่วนตัว” จากเบราว์เซอร์ เว็บแอปของธนาคารผมตอนล็อกอินแสดง ชื่อโฮสต์ ของผมคือ aluminium แล้วขอการยืนยันตัวตนสองขั้นตอน
ไม่รู้ว่ารู้ได้ยังไง ถ้าเข้าจากมือถือ มันยังเห็น SMS ที่มีโค้ดยืนยันสองขั้นตอนและวางให้อัตโนมัติได้ด้วย เป็นสภาพแวดล้อม Pixel+Chrome หรือ Linux+Chrome
ส่วนชื่อโฮสต์ยากกว่า ควรตรวจดูจริง ๆ ว่าเคยให้ข้อมูลนั้นแก่ธนาคารในรูปแบบอย่างชื่อผู้ใช้หรือเปล่า อยากรู้ด้วยว่าเป็นธนาคารไหน
บทความนี้แสดงให้เห็นชัดว่าทำไม เว็บแอปถึงไม่มีทางดีเท่าแอปเนทีฟได้
เว็บแอปเป็นโค้ดที่ “ไม่น่าเชื่อถือ” เสมอ ดังนั้นการเข้าถึงทรัพยากรของเครื่องโลคัลจึงถูกจำกัดแบบตามอำเภอใจและประดิษฐ์ขึ้น
ตอนนี้ไม่ใช่ยุคอินเทอร์เน็ตช่วงแรก ๆ ที่มีแต่ script kiddie อาละวาด และไม่ใช่พื้นที่ที่มีแต่พวกกี๊กใช้อีกแล้ว นี่คือยุคที่องค์กรที่เชื่อมโยงกับรัฐบาลโจมตีแรนซัมแวร์ต่อโครงสร้างพื้นฐานสำคัญเพื่อหาเงินสนับสนุนโครงการนิวเคลียร์ การเข้าถึงทรัพยากรใด ๆ บนเครื่องโลคัลได้นั่นแหละที่ทำให้เรื่องแบบนั้นเกิดขึ้นได้
เว็บแอปในเบราว์เซอร์สมัยใหม่ย่อมอยู่ในแซนด์บ็อกซ์ที่แข็งแรงกว่า แต่ในระบบปฏิบัติการสมัยใหม่ แอปเนทีฟก็ถูกปฏิบัติเป็นสิ่งที่ไม่น่าเชื่อถือเช่นกัน เมื่อรันแอปใหม่ จะมีไดอะล็อกยืนยันก่อนเข้าถึงสิ่งใดนอกไดเรกทอรีข้อมูลแอปที่ถูกแยกไว้
แต่ก็สงสัยว่า ส่วนขยาย Chrome ตัวไหนกันที่ทำให้มีคำเตือนเด้งขึ้นบน MacBook