- Das Web-Clipboard speichert auch einmal kopierte Daten gemeinsam in mehreren MIME-Repräsentationen, sodass jede App nur die benötigte Form zum Einfügen auswählen kann
- Die async Clipboard API ist im Wesentlichen auf
text/plain,text/htmlundimage/pngbeschränkt; Custom-Typen wieapplication/jsonwerden aus Sicherheitsgründen beim Schreiben abgelehnt - Die ältere Clipboard Events API kann beliebige Typ-Strings verarbeiten, aber das tatsächliche System-Clipboard wird nur innerhalb eines vertrauenswürdigen Nutzer-Events geändert
- Google Docs erzeugt mit
execCommand("copy")ein vertrauenswürdiges copy-Event, und Figma legt Custom-Daten in base64-kodierten HTML-Attributen ab, um Kopieren zwischen Browser und nativer App zu ermöglichen - Web Custom Formats öffnen Custom-Typen in der async Clipboard API über das Präfix
"web ", benötigen aber Chromium-basierte Browser und aktualisierte native Apps
Grundmodell des Web-Clipboards
- Das Clipboard kann für einen Kopiervorgang mehrere Repräsentationen (representations) zusammen mit MIME-Typen speichern
text/plain: einfacher Texttext/html: HTMLimage/png: PNG-Bild
- Google Docs liest die
text/html-Repräsentation, um Formatierungen wie Links, Schriftgröße und Farben beizubehalten - Apps wie VS Code, die nur den ursprünglichen Text benötigen, können ausschließlich die
text/plain-Repräsentation lesen
Typbeschränkungen der async Clipboard API
navigator.clipboard.read()prüft dietypeseines Clipboard-Eintrags und liest dann eine bestimmte Repräsentation, etwa mitgetType("text/html")navigator.clipboard.write()kann mehrereBlobs in einemClipboardItemmit MIME-Typen als Schlüssel sammeln und in einem Schritt schreiben- Wenn man
application/jsonalsClipboardItemschreiben will, tritt die AusnahmeType application/json not supported on writeauf - Die W3C-Clipboard-Spezifikation verlangt, dass Typen außer
text/plain,text/htmlundimage/pngbeimwriteabgelehnt werden application/jsonstand von 2012 bis 2021 in der Liste der verpflichtenden Datentypen, wurde aber mit w3c/clipboard-apis#155 entfernt- Vor der Änderung umfassten die verpflichtenden Datentypen 16 Typen zum Lesen und 8 zum Schreiben
- Die Liste wurde verkürzt, weil Browser aus Sicherheitsbedenken viele verpflichtende Typen nicht unterstützten
- Die Spezifikation warnt, dass nicht vertrauenswürdige Skripte Daten ins Clipboard legen könnten, die Schwachstellen lokaler Software auslösen; deshalb werden zulässige Typen beschränkt
Clipboard Events API und isTrusted
ClipboardEventtritt bei copy-, cut- und paste-Events auf und stellt über die EigenschaftclipboardDataeinDataTransfer-Objekt bereit- In einem copy-Event lassen sich Daten mit
e.clipboardData.setData("text/plain", "...")undsetData("text/html", "...")schreiben - In einem paste-Event kann man mit
e.clipboardData.getData("text/html")die HTML-Repräsentation lesen clipboardData.setData("application/json", json)wirft keine Ausnahme, und auch im paste-Handler lassen sich der Typapplication/jsonund der JSON-String sehenclipboardDataist eine deutlich ältere Schnittstelle als die async Clipboard API- Die async Clipboard API wurde 2017 in die Spezifikation aufgenommen
clipboardData,setDataundgetDatatauchten bereits in einem W3C-Entwurf von 2006 auf- Dieser Entwurf spiegelte stark Funktionen wider, die in Internet Explorer implementiert waren
- Da Internet Explorer 4 im Jahr 1997 erschien, kann man das
clipboardData-Interface zum Zeitpunkt des Artikels als mindestens 26 Jahre alte Funktion betrachten
- Ab der Spezifikation von 2011 kamen MIME-Typen hinzu, doch die Eigenschaft, dass als Typ-Argument für
setData()ein beliebiger String verwendet werden kann, besteht bis heute Event.isTrustedist eine schreibgeschützte Eigenschaft, die nur danntrueist, wenn das Event vom User Agent ausgelöst wurde- Ein copy-Event, bei dem der Nutzer Command+C drückt, hat
isTrustedauftrue - Ein mit
dispatchEvent()erzeugtes synthetisches Event hatisTrustedauffalse
- Ein copy-Event, bei dem der Nutzer Command+C drückt, hat
- Synthetische copy-/cut-Events können das System-Clipboard nicht verändern, und synthetische paste-Events können nicht auf echte System-Clipboard-Daten zugreifen
- Daher kann die Clipboard Events API zwar beliebige Typen verarbeiten, die Interaktion mit dem tatsächlichen Clipboard ist aber auf Handler für vom User Agent ausgelöste copy-/paste-Events beschränkt
Custom-Typen über einen Copy-Button schreiben
- Der Copy-Button im Rechtsklickmenü von Google Docs schreibt drei Repräsentationen ins Clipboard
text/plaintext/htmlapplication/x-vnd.google-docs-document-slice-clip+wrapped
- Die dritte Repräsentation ist ein Custom-Typ mit JSON-Daten und kann allein mit der async Clipboard API nicht geschrieben werden
- Google Docs ruft beim Klick auf den Copy-Button
document.execCommand("copy")auf execCommand("copy")kann programmatisch ein vertrauenswürdiges copy-Event auslösen, als hätte der Nutzer den Kopierbefehl ausgeführt- In Safari benötigt
execCommand("copy")eine aktive Auswahl, um ein copy-Event auszulösen- Man kann eine Auswahl erzeugen, indem man ein nicht leeres input zum DOM hinzufügt, es auswählt,
execCommand("copy")aufruft und es wieder entfernt
- Man kann eine Auswahl erzeugen, indem man ein nicht leeres input zum DOM hinzufügt, es auswählt,
Browser- und OS-Unterschiede beim Paste-Button
- Der Paste-Button von Google Docs verlangte unter macOS die Installation einer Erweiterung, funktionierte aber auf einem Windows-Notebook
- Mit
document.queryCommandSupported("paste")lässt sich prüfen, ob der paste-Befehl unterstützt wird- Unter macOS liefern Chrome und Firefox
false, Safaritrue - Unter Windows liefern Chrome und Edge
true, Firefoxfalse
- Unter macOS liefern Chrome und Firefox
- Safari zeigt einen Prompt zur Bestätigung des Einfügevorgangs an und macht so deutlich, dass die Website das Clipboard liest
- Warum Chrome unter Windows
execCommand("paste")erlaubt, unter macOS aber nicht, ist nicht geklärt - Google Docs fällt nicht auf die async Clipboard API zurück, wenn
execCommand("paste")nicht verwendet werden kann- Mit der async Clipboard API lässt sich die Repräsentation
application/x-vnd.google-[...]nicht lesen - Die HTML-Repräsentation enthält interne IDs
- Mit der async Clipboard API lässt sich die Repräsentation
HTML-basiertes Kopieren und Einfügen in Figma
- Der Copy-Button von Figma schreibt zwei Repräsentationen ins Clipboard:
text/plainundtext/html - Die HTML-Repräsentation enthält zwei leere
span-Elemente, jeweils mit den Attributendata-metadataunddata-buffer data-metadataist ein base64-kodierter JSON-String; nach dem Dekodieren erscheinen Werte wiefileKey,pasteIDunddataTypedata-bufferist schon beim Kopieren eines leeren Frames etwa 26.000 Zeichen lang und scheint linear mit der Menge des kopierten Inhalts zu wachsen- Dekodiert man
data-bufferaus base64, erhält man Binärdaten, die mitfig-kiwibeginnen; dabei handelt es sich um das Kiwi message format- Kiwi ist ein Format von Evan Wallace, Figma-Mitgründer und ehemaligem CTO
- Es wird zur Kodierung von
.fig-Dateien verwendet
- Mit dem von Evan Wallace veröffentlichten
.figfile parser lässt sichdata-bufferin eine.fig-Datei umwandeln und prüfen - Figma erzeugt eine kleine Figma-Datei, kodiert sie in base64 und legt sie im
data-buffer-Attribut eines leeren HTML-spanim Clipboard ab
Warum die HTML-Repräsentation genutzt wird
- Windows, macOS und Linux stellen unterschiedliche OS-Formate bereit, um Daten ins Clipboard zu schreiben
- Windows bietet
CF_HTMLfür HTML an - macOS bietet
NSPasteboard.PasteboardType.htmlan
- Windows bietet
- Einfacher Text, HTML und PNG-Bilder können auf mehreren Betriebssystemen auf Standardformate abgebildet werden
- Beliebige Typen wie
application/foo-barpassen nicht gut zu den gemeinsamen Formaten der OS-Clipboards - Browser schreiben solche Custom-Repräsentationen nicht in gemeinsame Clipboard-Formate des Betriebssystems, sondern speichern sie in browserspezifischen Custom-Clipboard-Formaten
- Dadurch funktionieren Copy & Paste mit Custom-Typen zwischen Browser-Tabs
- Zwischen Anwendungen funktioniert Copy & Paste damit nicht
- Nutzt man
text/html, wird es auf ein gemeinsames OS-Clipboard-Format abgebildet und ist für andere Anwendungen leichter lesbar - Figma verwendet
text/html, um Elemente zwischenfigma.comim Browser und der nativen Figma-App kopieren und einfügen zu können
Browserspezifische Speicherung von Custom-Daten
- Schreibt man unter macOS innerhalb eines copy-Events
text/plain,text/html,application/jsonundfoo bar bazund prüft dies mit Pasteboard Viewer, unterscheidet sich die Speicherung je nach Browser - Chrome fügt dem Pasteboard vier Einträge hinzu
public.html: HTML-Repräsentationpublic.utf8-plain-text: Plain-Text-Repräsentationorg.chromium.web-custom-data: Custom-Repräsentationorg.chromium.source-url: URL der Webseite, auf der kopiert wurde
- Firefox erzeugt ebenfalls
public.htmlundpublic.utf8-plain-text, schreibt Custom-Daten aber nachorg.mozilla.custom-clipdata- Anders als Chrome speichert Firefox keine source URL
- Safari erzeugt ebenfalls
public.htmlundpublic.utf8-plain-text; Custom-Daten schreibt es nachcom.apple.WebKit.custom-pasteboard-data- In diesem Eintrag speichert Safari auch die vollständige Liste der Repräsentationen inklusive Plain Text und HTML sowie die source URL
- Safari erlaubt Copy & Paste von Custom-Typen zwischen Browser-Tabs nur, wenn die Domain der source URL identisch ist
- In Chrome und Firefox ist diese Einschränkung nicht zu sehen
Raw Clipboard Access und Web Custom Formats
- Raw Clipboard Access war eine 2019 vorgeschlagene API, die Web-Anwendungen rohen Lese- und Schreibzugriff auf das native OS-Clipboard geben sollte
- Raw Clipboard Access adressierte das Problem, dass Web-Anwendungen auf wenige Formate beschränkt sind und Figma sowie Photopea dadurch nur schwer mit den meisten Bildformaten interoperieren können
- Dieser Vorschlag wurde wegen Sicherheitsbedenken wie Remote Code Execution in nativen Anwendungen nicht weiterverfolgt
- Web Custom Formats ist ein Vorschlag zur Nutzung von Custom-Typen in der async Clipboard API; Chromium implementierte ihn 2022
- Web Custom Formats werden verwendet, indem man dem MIME-Typ
"web "voranstellt- Beispiel:
web application/json - Zum Lesen verwendet man ebenfalls
read()der async Clipboard API undgetType("web application/json")
- Beispiel:
- Web Custom Formats schreiben zwei Arten von Daten in das native OS-Clipboard
- eine Zuordnung zwischen Datentypen und Namen von Clipboard-Einträgen
- je Datentyp einen Clipboard-Eintrag
- Unter macOS wird die Zuordnung in
org.w3.web-custom-format.mapgespeichert- Beispiel:
"application/json": "org.w3.web-custom-format.type-0"
- Beispiel:
- Die Einträge
org.w3.web-custom-format.type-[index]enthalten die Rohdaten des Blobs - Dieser Ansatz verhindert, dass Web-Anwendungen Rohdaten direkt in beliebige gewünschte OS-Clipboard-Formate schreiben, und vermeidet so die Sicherheitsprobleme von Raw Clipboard Access
- Dafür ist Interoperabilität mit alten nativen Anwendungen ohne Update kein Ziel
- Native Anwendungen müssen aktualisiert werden, um das Format der Web Custom Formats lesen zu können
- Web Custom Formats sind seit 2022 in Chromium-basierten Browsern verfügbar, andere Browser haben sie aber noch nicht implementiert
Option unsanitized
- Beim Lesen des Clipboards über die async Clipboard API können Browser die Daten bereinigen
- Sie können potenziell gefährliche script-Tags aus HTML entfernen
- Sie können PNG-Bilder neu kodieren, um Zip-Bomb-Angriffe zu vermeiden
- Die Option
unsanitizedvonread()erlaubt es, nicht bereinigte Daten anzufordern - Diese Option wird derzeit nur von Chromium-basierten Browsern unterstützt
- Sie wurde Ende 2023 hinzugefügt
- Andere Browser könnten sie künftig unterstützen, es gibt jedoch Feedback, dem zufolge Safari dafür eher unwahrscheinlich erscheint
Realistische Optionen
- Eine Methode, um Custom-Clipboard-Typen in allen Browsern zuverlässig zu nutzen, gibt es bislang nicht
- Der Ansatz von Figma, base64-Strings in die HTML-Repräsentation einzubetten, ist grob, kann aber mehrere Einschränkungen der Clipboard API umgehen und Custom-Daten über das Clipboard übertragen
- Web Custom Formats zeigen einen sichereren und praktikableren Weg für Custom-Typen, benötigen aber eine Implementierung in allen großen Browsern
1 Kommentare
Meinungen auf Hacker News
Guter Artikel. Jetzt verstehe ich, warum in Firefox auf dem Mac das Einfügen mit/ohne Formatierung in Google Docs nicht richtig funktionierte.
Interessant ist, dass Google eine veraltete API nutzt, um sein eigenes Inhaltsformat in die Zwischenablage zu legen.
Der zuverlässigste Weg, eigene App-Daten in die Zwischenablage zu bringen, scheint wie bei Figma darin zu bestehen, die Daten in HTML einzubetten.
Das hat auch den Vorteil, dass man über eine HTML-Nachricht sogar das Fehlverhalten definieren kann, falls die empfangende App es nicht unterstützt.
WordPress berührt dieses Problem ebenfalls. Wenn man im Editor-Modus mehrere Absätze ausschneidet und einfügt, kann das ziemlich kaputtgehen.
Vermutlich, weil die Absätze in Wirklichkeit unterschiedliche DIV-Bereiche sind und jeweils unter anderer Steuerlogik und anderen Effekten stehen.
Außerdem stufen Maschinen etwas, das ich für ASCII hielt, zu UTF-8 oder ISO Latin-1 hoch und verhalten sich wie Clippy: „Ich habe deine Anführungszeichen schöner gemacht, du wirst mir später danken.“ Das wollte ich nie. Wenn ich `this' gewollt hätte, hätte ich nicht 'this' getippt.
Als ich früher Student war, konnte JavaScript die Zwischenablage ohne Zustimmung des Nutzers lesen, und ich habe eine Website namens getpasted gebaut.
Sie fügte den Inhalt der Zwischenablage automatisch ein und lud ihn in eine öffentliche Datenbank hoch; natürlich gab es Leute, denen das nicht gefiel.
Als Projekt, das darauf aufmerksam machte, dass die Zwischenablage jederzeit gelesen werden konnte, war es aber okay.
Sindre Sorhus, der Entwickler von Pasteboard Manager[0], hat auch die Shortcut-Bibliothek Actions[1] sowie mehrere iPhone- und Mac-Apps erstellt. Ich weiß nicht, ob er auf HN ist.
0. https://apps.apple.com/us/app/pasteboard-viewer/id1499215709
https://www.npmjs.com/~sindresorhus
Die Kombination aus Breite und Tiefe ist besonders gut. Der Artikel behandelt sogar Unterschiede zwischen Systemen und Browsern und erklärt zugleich Fallstricke und Workarounds.
Im Zusammenhang mit dem Auslesen „privater“ Daten im Browser: Die Web-App meiner Bank zeigte beim Login meinen Hostnamen, aluminium, an und verlangte eine Zwei-Faktor-Authentifizierung.
Ich weiß nicht, wie sie das herausgefunden hat. Beim Zugriff vom Mobilgerät konnte sie auch die SMS mit dem 2FA-Code sehen und ihn automatisch einfügen. Das war in einer Pixel+Chrome- oder Linux+Chrome-Umgebung.
Der Hostname ist schwieriger. Du solltest prüfen, ob du der Bank diese Information nicht doch in irgendeiner Form, etwa als Benutzernamen, gegeben hast. Mich würde auch interessieren, welche Bank das war.
Dieser Artikel zeigt gut, warum Web-Apps nie so gut wie native Apps werden können.
Web-Apps sind immer „nicht vertrauenswürdiger“ Code, daher ist der Zugriff auf Ressourcen der lokalen Maschine willkürlich und künstlich eingeschränkt.
Wir leben nicht mehr im frühen Internet, in dem Script-Kiddies randalierten, und auch nicht in einem Raum, den nur Geeks nutzen. Es ist eine Zeit, in der staatlich verbundene Organisationen Ransomware-Angriffe auf kritische Infrastruktur durchführen, um Atomprogramme zu finanzieren. Beliebiger Zugriff auf lokale Ressourcen ermöglicht genau solche Dinge.
Web-Apps in modernen Browsern laufen naturgemäß in einer stärkeren Sandbox, aber in modernen Betriebssystemen werden auch native Apps als nicht vertrauenswürdig behandelt. Wenn man eine neue App startet, erscheint ein Bestätigungsdialog, bevor sie auf etwas außerhalb ihres isolierten App-Datenverzeichnisses zugreifen darf.
Mich würde allerdings interessieren, welche Chrome-Erweiterung auf dem MacBook diese Warnung ausgelöst hat.