Comment by cxr
6 months ago
In principle, that's something that could be allowed without giving access "to" Google/the site owner—even allowing the site author to provide their own functions for formatting and drawing the values—and thus could be allowed for _any_ website. Designing and implementing it is a fun technical problem, so it's a wonder why it wasn't, considering the motivations of a typical programmer (and those at Google especially).
How would you make an API accessible on the browser side but prevent the return values from being sent to the server? Somebody would surely find a way to use it for user fingerprinting.
Edit: I guess if you only want to make a local debug tool, you could make it callable only from a completely isolated sandbox. Maybe?
> How would you make an API accessible on the browser side but prevent the return values from being sent to the server?
Create an API for starting a "performance-metrics visualization Service Worker", that takes two things from a page as input:
1. the service-worker script URL
2. the handle of a freshly-allocated WebGL Canvas (which may or may not already be attached to the DOM, but which has never yet received any WebGL calls.) This Canvas will have its ownership moved to the Service Worker, leaving the object in the page as only an opaque reference to the Canvas.
The resulting Service Worker will live in a sandbox such that it 1. doesn't have network access, 2. can receive postMessage calls, but not make them; and 3. doesn't have any write access to any storage mechanism. Other than drawing on the Canvas, it's a pure consumer.
Also, obviously, this special sandbox grants the Service Worker the ability to access this performance API, with the metrics being measured in the context of the page that started the Worker.
The Service Worker is then free to use the info it gathers from making perf API calls, to draw metrics onto the moved Canvas. It's also free to change how/what it's drawing, or quit altogether, in response to control messages posted to it from the page.
The page can't introspect the moved Canvas to see what the Service Worker has drawn. All it can do is use the Canvas's now-opaque handle to attach/detach it to the DOM.
The worker could still send the data back to the page via side-channels.
For example by using up resources like the cpu, the gpu or ram in timed intervalls. The page would then probe for the performance fluctuations of these resources and decode the data from the pattern of the fluctuations.
5 replies →
After some of the replies, I gave this a bit more thought, and I came up with an entirely-different design that (IMHO) has a much better security model — and which I personally like a lot better. It eschews webpage-supplied Arbitrary Code Execution altogether, while still letting the user style the perf-visualization charts to match the "theme" of the embedding page. (Also, as a bonus, this version of the design leans more heavily on existing sandboxing features, rather than having to hypothesize new ones!)
1. Rather than having the page-perf API be a Web API "only available on specific origins" or "only available to weirdly-sandboxed Service Workers", just make it a WebExtension API. One with its own WebExtension manifest capability required to enable it; where each browser vendor would only accept WebExtensions requesting that particular capability into their Extension Stores after very thorough vetting.
(Or in fact, maybe browser vendors would never accept third-party WebExtensions with this capability into their Extension Stores; and for each browser, the capability would only be used in a single extension, developed by the browser vendor themselves. This would then be analogous to the existing situation, where the Web API is only available on a first-party domain controlled by the browser vendor; but as this would rely on the existing WebExtensions capabilities model, there would be no need for a separate one-off "WebAPI but locked to an origin" security-model. Also, unlike with a "WebAPI but locked to an origin" capability, you could play with this capability locally in an unpacked extension!)
2. Browsers that want to offer this "visualize the performance of the current page" ability as a "thing the browser can do", would just bundle their first-party WebExtension that holds this capability [and the "access current tab" capability] as a pre-installed + force-enabled component of the browser, hidden from the extensions management view. (I believe that this is already a thing browsers do — e.g. I believe Chrome implements its PDF viewer this way.)
3. This WebExtension would consist, at its core, of an extension page (https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Web...), that could be embedded into webpages as an iframe. This extension page would contain a script, which would use the WebExtension version of the page-perf API to continuously probe/monitor "the tab this instance of the extension-page document lives within." On receiving a perf sample, this script would then render the sample as a datapoint, in a chart that lives in some sense in the extension page's own DOM. There'd be no Service Worker necessary.
(Though, for efficiency reasons, the developer of this WebExtension might still want to split the perf-API polling out into a Service Worker, as a sort of "weak-reference perf provider" that the extension page subscribes to messages from + sends infrequent keepalive polls to. This would ensure that the Service Worker would unload — and so stop polling the page-perf API — both whenever the extension page's tab goes inactive, and whenever the browser unloads extension Service Workers generally [e.g. whenever the lid of a laptop is closed.] The page-perf API could itself be made to work this way... but it's easier to take advantage of the existing semantics of Service Worker lifetimes, no?)
4. But is there an existing API that allows a web-origin page to access/embed an extension page, without knowing the extension's (browser-specific) extension ID? Yes! Just like native apps can register "app intents", WebExtensions can register URI protocol handlers! (https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Web...) The performance-visualization WebExtension would register a protocol_handler for e.g. ext+pageperf:// , and then a page that wants to render its own perf would just create an <iframe allowTransparency="true" src="ext+pageperf://..." /> and shove it into the DOM. The privileged-origin-separation restrictions for iframes, would then stop the (web-origin, lower-privilege-level) page from introspecting the DOM inside the (extension-origin, higher-privilege-level) iframe — accomplishing the same thing that the "make the Canvas into an opaque handle" concept did in the previous design, but relying 100% on already-standardized security semantics of existing browser features.
---
But how would the webpage style the extension page to match itself? Well, that depends on what the extension page is doing to render the metrics.
An elegant assumption would be that the page-perf extension page is creating + incrementally updating an SVG DOM that lives embedded in the page's DOM — on each frame, appending new vector elements (e.g. bezier control-points) to that SVG's DOM to represent new datapoints in the time-series. (I believe e.g. Grafana's charting logic works this way.)
If this is the case, then all the parent page needs, is a way to pass a regular old CSS stylesheet to the extension page; which the extension page could then embed directly into its own <head>. Just have the parent page construct a <style> Element, and then call:
(And the beauty of doing that, is that once you embed the transferred <style> from the webpage into the extension page, the very same privileged-origin-separation logic will kick in again, preventing the extension pages from loading insecure web-origin content from an origin not in the extension's manifest's whitelisted origins. Which in turn means that, despite the webpage author being able to write e.g. `background: uri(...)` in their stylesheet, and despite the extension page doing nothing to explicitly filter things like that out of the stylesheet before loading it, the extension page would get to the "making a network request" part of applying that style, hit a security violation, and fail the load. Thereby neutering the ability of the page developer to use web-origin URL-references within a stylesheet as a side-channel for communicating anything about the metrics back to them!)
---
And all of this would be easily made cross-browser. You'd need a little standalone "Browser Support for Embedded Performance Visualizations" spec, that specifies two things:
1. a well-known URI scheme, for each browser's built-in page-perf extension to register a protocol_handler for. (Since it's standardized, they can drop the "ext+" part — it can just be `pageperf://`.)
2. a fixed structure for the page-perf extension page's metrics-visualization DOM — i.e. a known hierarchy of elements with specific `id` and `class` attributes (think CSS Zen Garden) — so that the same webpage-author-supplied stylesheets will work to style every browser's own metrics-chart implementation. (This would seem constraining, but remember that WebComponents exist. Standardize a WebComponent for the metrics chart, and how styles affect it. Browser vendors are then free to implement that WebComponent however they like in their own impl of the extension. Very similar to how browser vendors are free to implement the internals of a new HTML element, actually — in fact, in theory, for maximum efficiency, browsers could even implement this WebComponent's shadow-DOM in their renderers in terms of a custom "internal" HTML element!)
The browser has CORS and CSRF to sandbox similar activities to this.
Why not just ask for permission when needing that sort of data?
Some people wont give it and then they will have less data.
Those hypothetical programmers at Google could start by doing a Manifest V4 that would be like V3 but actually useful and privacy-respecting. I’ll believe it when it happens.