w3c / webappsec-permissions-policy

A mechanism to selectively enable and disable browser features and APIs
https://w3c.github.io/webappsec-permissions-policy/
Other
399 stars 155 forks source link

Proposal: allow grouping permissions by year #481

Open Seirdy opened 2 years ago

Seirdy commented 2 years ago

A comprehensive Permissions-Policy header to "opt out of everything":

accelerometer=(),ambient-light-sensor=(),attribution-reporting=(),autoplay=(),battery=(),bluetooth=(),browsing-topics=(),camera=(),ch-device-memory=(),ch-downlink=(),ch-dpr=(),ch-ect=(),ch-lang=(),ch-prefers-color-scheme=(),ch-rtt=(),ch-save-data=(),ch-ua=(),ch-ua-arch=(),ch-ua-bitness=(),ch-ua-full=(),ch-ua-full-version=(),ch-ua-full-version-list=(),ch-ua-mobile=(),ch-ua-model=(),ch-ua-platform=(),ch-ua-platform-version=(),ch-ua-reduced=(),ch-ua-wow64=(),ch-viewport-height=(),ch-viewport-width=(),ch-width=(),clipboard-read=(),clipboard-write=(),conversion-measurement=(),cross-origin-isolated=(),direct-sockets=(),display-capture=(),document-domain=(),encrypted-media=(),execution-while-not-rendered=(),execution-while-out-of-viewport=(),federated-credentials=(),focus-without-user-activation=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),hid=(),idle-detection=(),interest-cohort=(),join-ad-interest-group=(),keyboard-map=(),local-fonts=(),magnetometer=(),microphone=(),midi=(),navigation-override=(),otp-credentials=(),payment=(),picture-in-picture=(),publickey-credentials-get=(),run-ad-auction=(),screen-wake-lock=(),serial=(),shared-autofill=(),shared-storage=(),speaker-selection=(),storage-access-api=(),sync-script=(),sync-xhr=(),trust-token-redemption=(),unload=(),usb=(),vertical-scroll=(),wake-lock=(),web-share=(),window-placement=(),xr-spatial-tracking=()

Others have suggested an all permission; however, this isn't feasible because the meaning of all will change every time a new permission is added. Sites using all will not be forward-compatible.

My proposal: combine all these into a special permission called 2022. In 2023, new permissions might crop up; they won't be included in the 2022 permission. At the end of 2023, a new permission called 2023 can be created that will include 2022 and all permissions added in 2023.

The equivalent header content:

2022=()
marcoscaceres commented 2 years ago

I don't think this will work because what is supported per year will be user agent dependent (and unlikely to get agreement). It's also a bit of a footgun, because it doesn't actually tell you what is included per year.

ecki commented 2 years ago

You can also use a generation or version number, but I also think it needs an easy way to „deny all“ for hardening headers.

bershanskiy commented 2 years ago

For reference, this kind of issue is typically solved by using a library/middleware which automatically inserts headers. Basically, the developer just installs a library like Helmet for NodeJS Express or Talisman for Flask, and then the library can make educated guesses regarding the desired headers. These libraries/middlewares can provide an option to disable all permissions by default and permit only specified exceptions. For example, Flask supports both Permissions-Policy and its predecessor Feature-Policy at the same time. This approach would have the least compatibility fallout because then each project explicitly decides when to upgrade its middleware dependency and is actually in control of its own headers.

Seirdy commented 2 years ago

On Sun, Sep 04, 2022 at 02:34:22PM -0700, Anton Bershanskiy wrote:

For reference, this kind of issue is typically solved by using a library/middleware which automatically inserts headers.

This does not solve the issue of header bloat. Right now, the header is over 1kb and will only get larger with time.

-- Seirdy (https://seirdy.one)

bershanskiy commented 2 years ago

This does not solve the issue of header bloat. Right now, the header is over 1kb and will only get larger with time.

Is header bloat that big of a problem in practice? I believe that if the HTTP header remains the same within the entire HTTP/2 or HTTP/3 session, then the header will be sent only once and saved by the server, and then reused afterward. This article gives an example for HTTP/2. HTTP/1.1 does not explicitly support header compression, but it does support compressing the entire message, so headers are somewhat compressed too.

Seirdy commented 2 years ago

On Mon, Sep 05, 2022 at 03:12:23PM -0700, Anton Bershanskiy wrote:

Is header bloat that big of a problem in practice? I believe that if the HTTP header remains the same within the entire HTTP/2 or HTTP/3 session, then the header will be sent only once and saved by the server, and then reused afterward.

HPACK and QPACK are optional features that not all browsers and clients support. libcurl, for instance, supports neither. Moreover, this also requires HTTP/2 and HTTP/3 support; a quick look through a typical access log for a public service should reveal that HTTP/1.1 responses are still quite common.

This only applies to repeat visits. Initial visits will still have significant header bloat, making it difficult to initiate a connection/download for all blocking resources in the first round-trip.

This only addresses the performance issues of header bloat, but there's also the secondary issue of complexity (e.g. requiring middleware). As this header will only get bigger every time a new sensitive feature is standardized, we need some sort of forwards-compatible mechanism to keep it under control. A biennial meeting to figure out the set of implemented directives seemed like the most obvious choice to me.

-- Seirdy (https://seirdy.one)

annevk commented 2 years ago

Well,

  1. We are designing standards for the long term, not for short term problems such as HTTP/1.1 still seeing usage.
  2. If this is about "bloat" and thus performance, presumably HTTP/3 adoption will do more for that.
  3. As such, I think we should be comparing wins relative to HTTP/3 with the latest in header compression.
ecki commented 2 years ago

Another aspect, a deny-list approach is generally frowned upon in the security community. Why start it here. Yes the web might be of open nature, thats why a single header "lock everything i will request the stuff i need" sounds like a good compromise (even when it has the problem to define what "everything" means :)

annevk commented 2 years ago

Well, this is not a security feature. It's primarily a feature to delegate authority. It also allows imposing restrictions on oneself, but that is not the primary motivation. And safelisting would make it rather problematic to extend it going forward. E.g., it would make it hard-if-not-impossible to introduce a permission for <input type=file> as sites using safelisting might expect that to continue working.

darobin commented 1 year ago

One place where having a broad safelisting-based approach is when creating gateways to other systems such that their content is supposed be rendered at least to some degree but 1) it's potentially arbitrary and 2) you are comfortable occasionally breaking things that could be used for Bad Purposes™. I have the concern for (older-style, but that have to stick around) IPFS gateways. I could totally live with Permissions-Policy: anything-browsers-think-can-be-misused=().

annevk commented 1 year ago

The problem is that we don't know who will build upon it and wether or not they will hold it correctly.

Seirdy commented 1 year ago

I think we should be comparing wins relative to HTTP/3 with the latest in header compression.

@annevk I'm a browser "with the latest in header compression", fetching a web page. I race a TCP-based ALPN run against an HTTPS record lookup (Chromium's behavior). Either the HTTP/2 ALPN wins the race, or the HTTPS DNS record does not exist. Both are, and will remain, common scenarios. So I fetch the page over HTTP/2. This is the initial request; dynamic HPACK hasn't kicked in. I download a 1.56kb HTTP response header:

Permissions-Policy: accelerometer=(),ambient-light-sensor=(),attribution-reporting=(),autoplay=(),battery=(),bluetooth=(),browsing-topics=(),camera=(),ch-device-memory=(),ch-downlink=(),ch-dpr=(),ch-ect=(),ch-lang=(),ch-partitioned-cookies=(),ch-prefers-color-scheme=(),ch-prefers-reduced-motion=(),ch-rtt=(),ch-save-data=(),ch-ua=(),ch-ua-arch=(),ch-ua-bitness=(),ch-ua-full=(),ch-ua-full-version=(),ch-ua-full-version-list=(),ch-ua-mobile=(),ch-ua-model=(),ch-ua-platform=(),ch-ua-platform-version=(),ch-ua-reduced=(),ch-ua-wow64=(),ch-viewport-height=(),ch-viewport-width=(),ch-width=(),clipboard-read=(),clipboard-write=(),compute-pressure=(),conversion-measurement=(),cross-origin-isolated=(),direct-sockets=(),display-capture=(),document-domain=(),encrypted-media=(),execution-while-not-rendered=(),execution-while-out-of-viewport=(),focus-without-user-activation=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),hid=(),identity-credentials-get=(),idle-detection=(),interest-cohort=(),join-ad-interest-group=(),keyboard-map=(),local-fonts=(),magnetometer=(),microphone=(),midi=(),navigation-override=(),otp-credentials=(),payment=(),picture-in-picture=(),private-state-token-issuance=(),private-state-token-redemption=(),publickey-credentials-get=(),run-ad-auction=(),screen-wake-lock=(),serial=(),shared-autofill=(),smart-card=(),speaker-selection=(),storage-access=(),storage-access-api=(),sync-script=(),sync-xhr=(),trust-token-redemption=(),unload=(),usb=(),vertical-scroll=(),wake-lock=(),web-share=(),window-placement=(),xr-spatial-tracking=()

Now, through Alt-Svc or an HTTPS record lookup, I discover HTTP/3 support. I download a render-blocking asset over an upgraded HTTP/3 connection. This is the first HTTP/3 request; dynamic QPACK compression hasn't kicked in. I download a 1.56kb HTTP response header:

Permissions-Policy: accelerometer=(),ambient-light-sensor=(),attribution-reporting=(),autoplay=(),battery=(),bluetooth=(),browsing-topics=(),camera=(),ch-device-memory=(),ch-downlink=(),ch-dpr=(),ch-ect=(),ch-lang=(),ch-partitioned-cookies=(),ch-prefers-color-scheme=(),ch-prefers-reduced-motion=(),ch-rtt=(),ch-save-data=(),ch-ua=(),ch-ua-arch=(),ch-ua-bitness=(),ch-ua-full=(),ch-ua-full-version=(),ch-ua-full-version-list=(),ch-ua-mobile=(),ch-ua-model=(),ch-ua-platform=(),ch-ua-platform-version=(),ch-ua-reduced=(),ch-ua-wow64=(),ch-viewport-height=(),ch-viewport-width=(),ch-width=(),clipboard-read=(),clipboard-write=(),compute-pressure=(),conversion-measurement=(),cross-origin-isolated=(),direct-sockets=(),display-capture=(),document-domain=(),encrypted-media=(),execution-while-not-rendered=(),execution-while-out-of-viewport=(),focus-without-user-activation=(),fullscreen=(),gamepad=(),geolocation=(),gyroscope=(),hid=(),identity-credentials-get=(),idle-detection=(),interest-cohort=(),join-ad-interest-group=(),keyboard-map=(),local-fonts=(),magnetometer=(),microphone=(),midi=(),navigation-override=(),otp-credentials=(),payment=(),picture-in-picture=(),private-state-token-issuance=(),private-state-token-redemption=(),publickey-credentials-get=(),run-ad-auction=(),screen-wake-lock=(),serial=(),shared-autofill=(),smart-card=(),speaker-selection=(),storage-access=(),storage-access-api=(),sync-script=(),sync-xhr=(),trust-token-redemption=(),unload=(),usb=(),vertical-scroll=(),wake-lock=(),web-share=(),window-placement=(),xr-spatial-tracking=()

I've burned 3.2kb on what may be a small page, for one header. Rendering hasn't started yet. Add headers this is meant to complement (Document-Policy, CSP, etc.), all with an expanding list of directives, and header size becomes a problem. This is before we consider clients that lack HPACK/QPACK support (nearly all HTTP libraries I've used), or resources on other domains.

CSP had a good solution: grouping the fetch directives that existed at the time under default-src. Similarly, we can try defining a large set of permissions to group under one directive. Every few years, we could add a new meta-directive to keep forward- and backward-compatibility while also keeping header size from growing out of control.


POSSE note from https://seirdy.one/notes/2023/08/03/permissions-policy-header-bloat/

Seirdy commented 2 weeks ago

Just over a year later, a full invocation of the header that includes all Chromium permissions has grown from 1.56kb to 1.83kb:

Permissions-Policy: accelerometer=(),all-screens-capture=(),ambient-light-sensor=(),attribution-reporting=(),autoplay=(),bluetooth=(),browsing-topics=(),camera=(),captured-surface-control=(),ch-dpr=(),ch-device-memory=(),ch-downlink=(),ch-ect=(),ch-prefers-color-scheme=(),ch-prefers-reduced-motion=(),ch-prefers-reduced-transparency=(),ch-rtt=(),ch-save-data=(),ch-ua=(),ch-ua-arch=(),ch-ua-bitness=(),ch-ua-platform=(),ch-ua-model=(),ch-ua-mobile=(),ch-ua-form-factors=(),ch-ua-full-version=(),ch-ua-full-version-list=(),ch-ua-platform-version=(),ch-ua-wow64=(),ch-viewport-height=(),ch-viewport-width=(),ch-width=(),clipboard-read=(),clipboard-write=(),compute-pressure=(),controlled-frame=(),cross-origin-isolated=(),deferred-fetch=(),digital-credentials-get=(),direct-sockets=(),direct-sockets-private=(),display-capture=(),document-domain=(),encrypted-media=(),execution-while-out-of-viewport=(),execution-while-not-rendered=(),fenced-unpartitioned-storage-read=(),focus-without-user-activation=(),fullscreen=(),frobulate=(),gamepad=(),geolocation=(),gyroscope=(),hid=(),identity-credentials-get=(),idle-detection=(),interest-cohort=(),join-ad-interest-group=(),keyboard-map=(),local-fonts=(),magnetometer=(),media-playback-while-not-visible=(),microphone=(),midi=(),otp-credentials=(),payment=(),picture-in-picture=(),popins=(),private-aggregation=(),private-state-token-issuance=(),private-state-token-redemption=(),publickey-credentials-create=(),publickey-credentials-get=(),run-ad-auction=(),screen-wake-lock=(),serial=(),shared-autofill=(),shared-storage=(),shared-storage-select-url=(),smart-card=(),speaker-selection=(),storage-access=(),sub-apps=(),sync-xhr=(),unload=(),usb=(),usb-unrestricted=(),vertical-scroll=(),web-app-installation=(),web-printing=(),web-share=(),window-management=(),xr-spatial-tracking=()

My point about gradual growth of this header is evidenced by the the history of the Chromium DevTools generated protocol.ts file.