Webクリップボードとさまざまな種類のデータ保存方式
(alexharri.com)- Webクリップボードは、一度コピーしたデータを複数のMIME表現として同時に保存でき、アプリごとに必要な形式だけを選んで貼り付けられるようにする
- async Clipboard APIは
text/plain、text/html、image/pngを中心に制限されており、application/jsonのようなカスタム型はセキュリティ上の理由で書き込みが拒否される - 古いClipboard Events APIは任意の型文字列を扱えるが、実際のシステムクリップボードは信頼されたユーザーイベントの中でのみ変更される
- Google Docsは
execCommand("copy")で信頼されたcopyイベントを生成し、Figmaはカスタムデータをbase64のHTML属性に入れてブラウザとネイティブアプリ間のコピーを処理する - Web Custom Formatsは
"web "接頭辞でasync Clipboard APIのカスタム型を開放するが、Chromiumベースのブラウザと更新済みのネイティブアプリが必要になる
Webクリップボードの基本モデル
- クリップボードは、1回のコピー操作に対して複数の**表現(representation)**をMIMEタイプとともに保存できる
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()は、MIMEタイプをキーにするClipboardItemに複数のBlobを入れて一度に書き込める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イベントで発生し、clipboardDataプロパティでDataTransferオブジェクトを提供する- copyイベントでは
e.clipboardData.setData("text/plain", "...")、setData("text/html", "...")でデータを書き込める - pasteイベントでは
e.clipboardData.getData("text/html")でHTML表現を読み取れる clipboardData.setData("application/json", json)は例外を投げず、pasteハンドラでもapplication/json型とJSON文字列を確認できるclipboardDataはasync Clipboard APIよりはるかに古いインターフェースである- async Clipboard APIは2017年に仕様へ追加された
clipboardData、setData、getDataは2006年のW3C草案にすでに登場している- その草案はInternet Explorerに実装されていた機能を大きく反映している
- Internet Explorer 4は1997年にリリースされたため、
clipboardDataインターフェースは執筆時点で少なくとも26年の歴史がある機能と見なせる
- 2011年仕様からMIMEタイプが導入されたが、
setData()の型引数にどんな文字列でも使える特性は現在も維持されている Event.isTrustedは、イベントがユーザーエージェントによって発生した場合にのみtrueになる読み取り専用プロパティである- ユーザーがCommand+Cを押したcopyイベントは
isTrustedがtrue dispatchEvent()で作られた合成イベントはisTrustedがfalse
- ユーザーがCommand+Cを押したcopyイベントは
- 合成copy/cutイベントはシステムクリップボードを変更できず、合成pasteイベントは実際のシステムクリップボードデータにアクセスできない
- したがってClipboard Events APIは任意の型を扱えるが、実際のクリップボードとの相互作用はユーザーエージェントが発生させたcopy/pasteイベントハンドラ内に限定される
Copyボタンでカスタム型を書く
- Google Docsの右クリックメニューのCopyボタンは、3つの表現をクリップボードに書き込む
text/plaintext/htmlapplication/x-vnd.google-docs-document-slice-clip+wrapped
- 3つ目の表現はJSONデータを含むカスタム型であり、async Clipboard APIだけでは書き込めない
- Google DocsはCopyボタンをクリックすると
document.execCommand("copy")を呼び出す execCommand("copy")は、ユーザーがコピーコマンドを実行したかのように信頼されたcopyイベントをプログラム的に発生させられる- Safariでは、
execCommand("copy")がcopyイベントを発生させるにはアクティブな選択範囲が必要である- 空でないinputをDOMに追加して選択し、その後
execCommand("copy")を呼び出して削除する方法で選択範囲を作れる
- 空でないinputをDOMに追加して選択し、その後
Pasteボタンのブラウザ・OS差異
- Google DocsのPasteボタンは、macOSでは拡張機能のインストールを要求したが、WindowsノートPCでは動作した
document.queryCommandSupported("paste")でpasteコマンドのサポート有無を確認できる- macOSではChromeとFirefoxが
false、Safariがtrue - WindowsではChromeとEdgeが
true、Firefoxがfalse
- macOSではChromeとFirefoxが
- Safariはpaste動作を確認するプロンプトを表示し、Webサイトがクリップボードを読むことを明確に示す
- ChromeがWindowsでは
execCommand("paste")を許可し、macOSでは許可しない理由は確認されていない - Google Docsは
execCommand("paste")を使えない場合、async Clipboard APIへフォールバックしない- async Clipboard APIでは
application/x-vnd.google-[...]表現を読めない - HTML表現には内部IDが含まれている
- async Clipboard APIでは
FigmaのHTMLベースのコピー・貼り付け
- FigmaのCopyボタンは、
text/plainとtext/htmlの2つの表現をクリップボードに書き込む - HTML表現には空の
spanが2つあり、それぞれdata-metadata、data-buffer属性を持つ data-metadataはbase64エンコードされたJSON文字列で、デコードするとfileKey、pasteID、dataTypeのような値が出てくるdata-bufferは空のフレームだけをコピーしても約26,000文字あり、コピーされたコンテンツ量に応じて長さが線形に増えるように見えるdata-bufferをbase64デコードするとfig-kiwiで始まるバイナリデータが現れ、これはKiwi message formatである- KiwiはFigma共同創業者で元CTOのEvan Wallaceが作った形式である
.figファイルのエンコーディングに使われている
- Evan Wallaceが公開した
.figfile parserを使うと、data-bufferを.figファイルに変換して確認できる - Figmaは小さなFigmaファイルを作り、それをbase64でエンコードして、空のHTML
spanのdata-buffer属性に入れ、クリップボードへ保存する
HTML表現を使う理由
- Windows、macOS、Linuxは、クリップボードへデータを書き込むための異なるOS形式を提供している
- WindowsはHTML用に
CF_HTMLを提供する - macOSは
NSPasteboard.PasteboardType.htmlを提供する
- WindowsはHTML用に
- プレーンテキスト、HTML、PNG画像は複数のOSで標準形式にマッピングできる
application/foo-barのような任意の型は、OSクリップボードの共通形式とうまく適合しない- ブラウザはこのようなカスタム表現をOSの共通クリップボード形式には書き込まず、ブラウザごとのカスタムクリップボード形式に保存する
- その結果、カスタム型はブラウザタブ間ではコピー・貼り付けできる
- アプリケーション間ではコピー・貼り付けできない
text/htmlを使えばOS共通のクリップボード形式にマッピングされるため、他のアプリケーションが読み取りやすい- Figmaは
text/htmlを使って、ブラウザのfigma.comとネイティブのFigmaアプリの間で要素をコピー・貼り付けできる
ブラウザごとのカスタムデータ保存方式
- macOSでcopyイベント内に
text/plain、text/html、application/json、foo bar bazを書き込み、Pasteboard Viewerで確認すると、ブラウザごとに保存方式が異なる - ChromeはPasteboardに4項目を追加する
public.html: HTML表現public.utf8-plain-text: プレーンテキスト表現org.chromium.web-custom-data: カスタム表現org.chromium.source-url: コピーが実行されたWebページの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は2019年に提案されたAPIで、WebアプリケーションにネイティブOSクリップボードの生の読み書きアクセスを提供しようとした
- Raw Clipboard Accessは、Webアプリケーションが少数の形式に制限されているため、FigmaやPhotopeaが大半の画像形式と相互運用しにくいという問題を扱っていた
- この提案は、ネイティブアプリケーションでのリモートコード実行のようなセキュリティ上の懸念により、それ以上進展しなかった
- Web Custom Formatsは、async Clipboard APIでカスタム型を書き込むための提案であり、Chromiumは2022年にこれを実装した
- Web Custom Formatsは、MIMEタイプの前に
"web "を付けて使う- 例:
web application/json - 読み取り時もasync Clipboard APIの
read()とgetType("web application/json")を使う
- 例:
- Web Custom Formatsは、ネイティブOSクリップボードに2種類のデータを書き込む
- データ型とクリップボード項目名のマッピング
- 各データ型ごとのクリップボード項目
- macOSでは、マッピングは
org.w3.web-custom-format.mapに保存される- 例:
"application/json": "org.w3.web-custom-format.type-0"
- 例:
org.w3.web-custom-format.type-[index]項目には、Blobの未加工データが入る- この方式により、Webアプリケーションが望むOSクリップボード形式へ未加工データを直接書き込めなくなり、Raw Clipboard Accessのセキュリティ問題を回避する
- その代わり、レガシーなネイティブアプリケーションと更新なしで相互運用することは目標としていない
- ネイティブアプリケーション側がWeb Custom Formats形式を読めるよう更新される必要がある
- Web Custom Formatsは2022年からChromiumベースのブラウザで利用できるが、他のブラウザはまだ実装していない
unsanitizedオプション
- async Clipboard APIでクリップボードを読むとき、ブラウザはデータをサニタイズすることがある
- HTMLから危険な可能性のあるscriptタグを削除することがある
- zip bomb攻撃を避けるためPNG画像を再エンコードすることがある
read()のunsanitizedオプションを使うと、サニタイズされていないデータを要求できる- このオプションは現在Chromiumベースのブラウザでのみサポートされる
- 2023年末に追加された
- 他のブラウザも将来サポートする可能性はあるが、Safariは可能性が低そうだというフィードバックがある
現実的な選択肢
- すべてのブラウザでうまく動作するカスタムクリップボード型の書き込み方法は、まだ存在しない
- Figmaのようにbase64文字列をHTML表現の中へ入れる方法は粗削りだが、Clipboard APIのさまざまな制限を回避してカスタムデータをクリップボードで受け渡しできる
- Web Custom Formatsは、カスタム型をより安全かつ実用的に扱う方向性を示しているが、主要ブラウザ全体での実装が必要である
1件のコメント
Hacker Newsの意見
良い記事。Mac版FirefoxでGoogle Docsに書式あり/なしで貼り付けがうまく動かなかった理由が理解できた
Googleがクリップボードに独自のコンテンツ形式を入れようとして廃止されたAPIを使っている点が興味深い
カスタムアプリのデータをクリップボードに送る最も信頼できる方法は、FigmaのようにHTML内にデータを埋め込む方式のようだ
受け取るアプリが対応していないときに、HTMLメッセージとしてフォールバック動作まで定義できる利点もある
WordPressもこの問題に触れている。エディターモードで複数の段落を切り貼りすると大きく壊れることがある
おそらく段落が実際には別々のDIV領域で、それぞれ異なる制御ロジックや効果の下にあるからだろう
さらに、マシンが自分ではASCIIだと思っていたものをUTF-8やISO Latin-1にしてしまい、Clippyのように「引用符をもっと見栄えよくしておいたから、あとで感謝してね」みたいな動作をすることがあるが、そんなことは望んでいない。
this'が欲しかったなら、'this'と打っていないはずだ昔学生だったころ、JavaScriptがユーザーの同意なしにクリップボードを読めて、getpastedというウェブサイトを作った
クリップボードを自動で貼り付けて公開データベースに載せるサイトで、当然それを嫌う人もいた
それでも、いつでもクリップボードを読めるという事実を知らせるプロジェクトとしては悪くなかった
Pasteboard Manager[0]の作者であるSindre Sorhusは、Actions[1]ショートカットライブラリや複数のiPhone・Macアプリも作っている人物。HNにいるかどうかは分からない
0. https://apps.apple.com/us/app/pasteboard-viewer/id1499215709
https://www.npmjs.com/~sindresorhus
範囲と深さの組み合わせが特に良い。システム・ブラウザーごとの差異まで扱いながら、落とし穴と回避方法も一緒に説明してくれる
ブラウザーが「非公開」のデータを取得する話に関連して、自分の銀行のウェブアプリがログイン時に自分のホスト名であるaluminiumを表示し、2段階認証を要求してきた
どうやって知ったのか分からない。モバイルでアクセスすると、2段階認証コード入りのSMSも読んで自動で貼り付けられた。Pixel+ChromeまたはLinux+Chrome環境だった
ホスト名のほうはもっと難しい。本当にその情報をユーザー名のような形で銀行に渡していないのか確認したほうがいい。どこの銀行なのかも気になる
この記事は、なぜウェブアプリがネイティブアプリと同じくらい良くなれないのかをよく示している
ウェブアプリは常に「信頼できない」コードなので、ローカルマシンのリソースへのアクセスが恣意的かつ人為的に制限される
今はスクリプトキディが暴れていた初期インターネット時代でもなければ、変わり者だけが使う空間でもない。政府と結びついた組織が核開発資金を得るために重要インフラへランサムウェア攻撃を仕掛ける時代だ。ローカルマシンの任意のリソースへのアクセスは、まさにそうしたことを可能にする
最新ブラウザー上のウェブアプリは当然より強いサンドボックス内にあるが、最新のオペレーティングシステムではネイティブアプリも信頼されない対象として扱われる。新しいアプリを実行すると、隔離されたアプリデータディレクトリの外にアクセスする前に確認ダイアログが表示される
ところで、MacBookで警告が出ていたChrome拡張機能が何だったのか気になる