Skip to content

Commit 4c6acfc

Browse files
authored
Add the case of non-persistent clipboard permissions (#5)
1 parent 7afe27e commit 4c6acfc

File tree

1 file changed

+45
-24
lines changed

1 file changed

+45
-24
lines changed

README.md

+45-24
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,24 @@ so that:
4444
2. When a user copies something on the remote machine and switches away from the VDI app, they can paste the copied
4545
content locally.
4646

47-
Without `contentsId()`, there are two primary ways to achieve the first scenario:
47+
Without `contentsId()`, there are three primary ways to achieve the first scenario:
4848

49+
- Expose an additional "synchronize clipboard" button to the user - which, on click, would send the current clipboard to
50+
the remote machine.
4951
- Upon refocusing the VDI app, automatically send the content from the local clipboard to the remote machine.
5052
- Upon refocusing the VDI app, read the clipboard contents, compare them with the last known state, and send to the
5153
remote machine only if they have changed.
5254

53-
Neither of these approaches is optimal (especially with large clipboard contents), and additional challenges related to
54-
sanitization and encoding make it difficult to directly compare the clipboard contents byte-by-byte with previously
55-
received data.
55+
Neither of these approaches is both optimal (especially with large clipboard contents) and user-friendly, and additional
56+
challenges related to sanitization and encoding make it difficult to directly compare the clipboard contents
57+
byte-by-byte with previously received data.
58+
59+
Moreover, only the first option is viable in browsers that do not implement persistent clipboard permissions. There, the
60+
user would have to consciously remember to click on a button every time they have before copied something - which is
61+
problematic, as the system clipboard is a tool that most people use intuitively and intensively. A good example of this
62+
challenge could be [Cameyo](https://cameyo.com/), which essentially streams individual applications from the remote
63+
servers as Progressive Web Apps. Alt-tabbing from one like-native application to another and having to remember whether
64+
you have copied anything or not significantly degrades user experience.
5665

5766
### What is the optimal solution then?
5867

@@ -64,13 +73,23 @@ and
6473
[Wayland](https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/platform/wayland/host/wayland_data_device.cc;drc=d815f515138991af2aa5b1d07c64906fd8a7366b;bpv=1;bpt=1;l=182?gsn=OnSelection&gs=KYTHE%3A%2F%2Fkythe%3A%2F%2Fchromium.googlesource.com%2Fcodesearch%2Fchromium%2Fsrc%2F%2Fmain%3Flang%3Dc%252B%252B%3Fpath%3Dui%2Fozone%2Fplatform%2Fwayland%2Fhost%2Fwayland_data_device.cc%23KBIABXwYhD42mocIlezMjghFMtoChm0IKDja7p09J9o),
6574
[ChromeOS](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/clipboard/clipboard_non_backed.cc;drc=65c747e508657f16ca3d0905ab1e11115f5a5ff1;l=286),
6675
[Android](https://developer.android.com/reference/android/content/ClipboardManager.OnPrimaryClipChangedListener) and
67-
[iOS](https://developer.apple.com/documentation/uikit/uipasteboard/changecount)) offer efficient ways to track clipboard content changes
68-
without directly reading the data. This is often achieved through clipboard sequence numbers or change notifications.
69-
The `navigator.clipboard.contentsId()` API aims to leverage these capabilities. It allows websites to request a numeric
70-
token (a 128-bit integer) representing the current clipboard state. If this token differs from a previously retrieved
71-
one, it indicates that the clipboard contents have changed between the two calls. Importantly, this operation has a
72-
constant time complexity (O(1)), independent of the clipboard's size. Therefore, even frequent checks (e.g., on window
73-
refocus) remain efficient, even when dealing with large amounts of copied data.
76+
[iOS](https://developer.apple.com/documentation/uikit/uipasteboard/changecount)) offer efficient ways to track clipboard
77+
content changes without directly reading the data. This is often achieved through clipboard sequence numbers or change
78+
notifications. The `navigator.clipboard.contentsId()` API aims to leverage these capabilities. It allows websites to
79+
request a numeric token (a 128-bit integer) representing the current clipboard state. If this token differs from a
80+
previously retrieved one, it indicates that the clipboard contents have changed between the two calls. Importantly, this
81+
operation has a constant time complexity (O(1)), independent of the clipboard's size. Therefore, even frequent checks
82+
(e.g., pasting quickly many times in an online document editor or checking on every refocus whether to show "synchronize
83+
clipboard" button) remain efficient, even when dealing with large amounts of copied data.
84+
85+
This could help greatly in making web VDI clients work smoothly in browsers that base clipboard access on user
86+
activation - as the site could without activation check _whether_ the clipboard has changed and only then display a
87+
"click ctrl+v to synchronize clipboard" notification or a "click here to synchronize clipboard" button, taking the
88+
burden of tracking this from the user while still not having too much access to the clipboard.
89+
90+
Moreover, above approach at enabling apps to work better without broad persistent clipboard permissions could be
91+
extended to a lot more applications, for example online editors, that could using this minize the number of `read()`
92+
calls (as they prompt user to make additional action) and call it only if the clipboard has changed from the last time.
7493

7594
## Goals
7695

@@ -86,9 +105,11 @@ refocus) remain efficient, even when dealing with large amounts of copied data.
86105

87106
## Token stability across tabs or app windows
88107

89-
One of the goals of this API is to enable cross-app synchronization of clipboard \- so this should be as close to the
90-
stability of the clipboard itself as possible. So, every site under the same browser process should get the same token
91-
from calling `contentsId()`.
108+
One of the goals of this API is to enable cross-tab synchronization of clipboard on the scope of one origin \- so this
109+
should be as close to the stability of the clipboard itself as possible without providing cross-site fingerprinting
110+
surface. So, every tab of the same origin under the same
111+
[partition](https://w3ctag.github.io/privacy-principles/#dfn-partition) should get the same token from calling
112+
`contentsId()`.
92113

93114
## How to use it?
94115

@@ -109,7 +130,8 @@ window.addEventListener("focus", () => {
109130
navigator.clipboard.contentsId().then((token) => {
110131
if (token !== lastToken) {
111132
// Clipboard contents have changed!
112-
// Send to remote machine
133+
// Display the "synchronize clipboard" button or "press ctrl+v" notification to the user,
134+
// or just read the clipboard if you have the persistent clipboard permission.
113135
}
114136
lastToken = token;
115137
});
@@ -144,17 +166,16 @@ enable heuristics to make this invisible in most cases, but will not fix it comp
144166

145167
## Security & Privacy considerations
146168

147-
This should be under the same restrictions as the `navigator.clipboard.read()`:
169+
This in of itself does not provide the website with any new substantial information about the user. The only potential
170+
danger is a new fingerprinting surface. To remediate this:
148171

149-
- It should require `clipboard-read` permissions and request them on call.
150-
- It should be available only while the tab has focus.
172+
- This should be available only when the document is in focus (same as `navigator.clipboard.read()`).
173+
- The ID returned by this should be unique to the origin calling the method and change every time the site data for it
174+
is deleted.
151175

152-
Thus, it doesn’t expose any new not-available-before security-sensitive information. The only potential attack vector
153-
would be correlating different sessions with the same user based on the token, which provides a more precise way of
154-
ensuring across sessions that those to clipboards are in fact the same user. In practice however, this could be done by
155-
just re-reading the clipboard contents and comparing them, especially across changes \- which is possible already.
156-
Correlating users across sites by the origins that have clipboard permissions is already trivially easy and existence of
157-
this API does not change this state significantly.
176+
In this way, correlation of users cross-site should be impossible based on either the number itself or the exact timing
177+
of this number changing. Hence, this API should not provide any substantially new information to the site except a hint
178+
when to best call `read()` so that it's optimal and user-friendly.
158179

159180
## Alternatives
160181

0 commit comments

Comments
 (0)