Summary
The instrumentation sockets that carry all measurement records from the
WebExtension to the Python StorageController (and the reverse control channel)
are loopback-only, random-ephemeral-port, and not reachable from web content or
off-host — but they are unauthenticated and carry no transport security.
Concretely:
StorageController binds ("localhost", 0) (AF_INET, random port) and hands
the (host, port) to the extension via the profile directory
(browser_params.json). The extension's reverse listener binds
init(-1, true, -1) (loopback-only, random port) and publishes its port via
extension_port.txt.
- The first client message is just a name string (
Browser-<id> /
custom_command), not a secret. There is no per-connection token.
Impact
Any local process running as the same (or a more privileged) user, during
the window the port is open, can:
- Connect to the
StorageController loopback port and inject/forge framed
records (>Lc header + JSON [instrument, record]), poisoning the dataset or
forging Initialize/Finalize control events.
- In the dill-enabled in-process path, send a
d-tagged frame. _parse in
openwpm/socket_interface.py deserializes d frames via dill.loads, which
is full pickle — i.e. arbitrary code execution in the StorageController
process.
Risk assessment
- Acceptable for single-tenant deployments (the typical OpenWPM measurement
host), where there is no untrusted local user. The sockets are not web-reachable
and not reachable off-host, so this is a same-user local-process threat only.
- A real concern on shared / multi-tenant hosts, where an unprivileged
co-tenant could poison the dataset or, via the dill path, achieve RCE.
This is documented as a known issue in openwpm/socket_interface.py and
docs/Architecture-Internals.md. The current remediation decision is to treat it
as a documented single-tenant known-issue and not change the wire protocol.
Possible hardening (follow-up)
- Add a per-connection shared secret: the
StorageController generates a random
64-bit token, writes it alongside the address in the (filesystem-protected)
profile dir, and requires it as the first framed message.
- Stop accepting
d (dill) frames from any peer that has not authenticated, or
drop dill on the wire entirely in favour of a non-code-executing format for
the in-process TaskManager -> StorageController path.
References
- Audit: privileged
sockets WebExtension experiment (internal).
- Related OpenWPM threat-model concern about local data injection.
Summary
The instrumentation sockets that carry all measurement records from the
WebExtension to the Python
StorageController(and the reverse control channel)are loopback-only, random-ephemeral-port, and not reachable from web content or
off-host — but they are unauthenticated and carry no transport security.
Concretely:
StorageControllerbinds("localhost", 0)(AF_INET, random port) and handsthe
(host, port)to the extension via the profile directory(
browser_params.json). The extension's reverse listener bindsinit(-1, true, -1)(loopback-only, random port) and publishes its port viaextension_port.txt.Browser-<id>/custom_command), not a secret. There is no per-connection token.Impact
Any local process running as the same (or a more privileged) user, during
the window the port is open, can:
StorageControllerloopback port and inject/forge framedrecords (
>Lcheader + JSON[instrument, record]), poisoning the dataset orforging
Initialize/Finalizecontrol events.d-tagged frame._parseinopenwpm/socket_interface.pydeserializesdframes viadill.loads, whichis full pickle — i.e. arbitrary code execution in the
StorageControllerprocess.
Risk assessment
host), where there is no untrusted local user. The sockets are not web-reachable
and not reachable off-host, so this is a same-user local-process threat only.
co-tenant could poison the dataset or, via the dill path, achieve RCE.
This is documented as a known issue in
openwpm/socket_interface.pyanddocs/Architecture-Internals.md. The current remediation decision is to treat itas a documented single-tenant known-issue and not change the wire protocol.
Possible hardening (follow-up)
StorageControllergenerates a random64-bit token, writes it alongside the address in the (filesystem-protected)
profile dir, and requires it as the first framed message.
d(dill) frames from any peer that has not authenticated, ordrop
dillon the wire entirely in favour of a non-code-executing format forthe in-process
TaskManager -> StorageControllerpath.References
socketsWebExtension experiment (internal).