New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Proposal: Client Hints delegation #129

Open
igrigorik opened this Issue Jan 19, 2018 · 26 comments

Comments

Projects
None yet
9 participants
@igrigorik
Copy link
Member

igrigorik commented Jan 19, 2018

Client Hints defines a mechanism for UA+server to negotiate a set of HTTP request header fields that can be used for proactive content negotiation. For example, the server can request that the client send a DPR request header field such that it can pick the right image / CSS / HTML markup.

The site author must explicitly opt-in and specify which hints they want to receive via the Accept-CH header — e.g. Accept-CH: DPR response header indicates that the UA should send the DPR hint. Optionally, the site can also request that the UA remember this preference for a specified period of time, via Accept-CH-Lifetime header.


One existing limitation that came up in privacy review is that CH does not expose 'delegation controls' for 1P vs 3P resources; CH does not provide a means for 1P to specify which 3P's should be allowed to request various hints. I believe (and propose :)) that Feature Policy is the right tool for the job to address this: we can define each hint as a policy-controlled feature.

For example, enumerating existing hints today...

  • Hint: DPR → FP: ch-dpr with default policy of self.
  • Hint: Width → FP: ch-width with default policy of self.
  • Hint: Viewport-Width → FP: ch-viewport-width with default policy of self.
  • Hint: Save-Data → FP: ch-save-data with default policy of *.

In effect, the 1P has to first explicitly opt-in to receive any of the above via Accept-CH mechanism described above, and by default this opt-in would apply to 1P resources only. If the origin wishes to delegate permission to 3P's to request some or all of the same hints, it would then whitelist them via a custom Feature-Policy with above directives.

WDYT?

/cc @clelland @arturjanc @tarunban @yoavweiss

@arturjanc

This comment has been minimized.

Copy link

arturjanc commented Jan 22, 2018

I, perhaps predictably, like this proposal -- it seems like a good compromise between allowing authors to use client hints (and delegate access to trusted third parties) and protecting users' privacy (i.e. the potential to overshare data available in hints, which is addressed this integration with Feature Policy).

@clelland

This comment has been minimized.

Copy link
Collaborator

clelland commented Jan 23, 2018

So, with this model --

  1. The client requests a 1P page, the 1P responds with an Accept-CH header, and also a FP header which designates some 3P origin as 'allowed to request hints'.

  2. Subsequent requests to the 1P will include the acceptable hints.

  3. If the 1P embeds a frame from the 3P, no hints will be sent on the initial request, but the response from the 3P may also come back with an Accept-CH header, and if it does, subsequent requests will then include those hints (the intersection of the hints the 3P requested and the hints that the 1P allowed by policy).

  4. If a given hint is not allowed by policy in a frame, it will never be sent with requests originating withing that frame, no matter the origin, or how deeply the frame is embedded (this is a guarantee that FP makes, that once disabled, no mechanism should be able to reenable a feature within that frame)

Does that make sense?

Questions:

  • Is it possible for the 1P to not request any hints itself through an Accept-CH header, but to allow them for a 3P?

  • Feature policy enables an allow attribute on the <iframe> element. With this, a scenario like this is possible:

<body>
  <!-- resource in frame will be sent dpr hints if requsted -->
  <iframe src="https://example.com" allow="ch-dpr"></iframe>

  <!-- requests from this frame will not be sent with hints -->
  <iframe src="https://example.com"></iframe> 
</body>

(even though those frames frame pages from the same origin) Is this per-frame decision possible with CH, or is the hint/no-hint decision always scoped per-origin?

@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented Jan 23, 2018

Re, 1-4: yes, I think that's exactly right.

Is it possible for the 1P to not request any hints itself through an Accept-CH header, but to allow them for a 3P?

I think so and it's a reasonable thing to do. For example, I may rely on a CDN to perform image or other type of optimization that can benefit from one or more hints, but I may not need hints on my origin directly. In this case I would simply specify an FP policy on my (1P) response enabling the 3P to request access to hints.

Is this per-frame decision possible with CH, or is the hint/no-hint decision always scoped per-origin?

As defined by the CH spec, scoped per-origin. Do all features map to allow attributes? To start, I'm OK if we said allow attribute is not supported for CH, but open to other options too.

igrigorik added a commit to httpwg/http-extensions that referenced this issue Jan 25, 2018

note for explicit hint delegation for 3P origins
Per discussion in #372, add (optional) suggestion to enforce explicit
delegation for 3P origins. For example, a UA may use Feature Policy to
implement this — see w3c/webappsec-feature-policy#129.
@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented Jan 25, 2018

@clelland as a sanity check, does this proposal sound reasonable to you from a high-level perspective? There's definitely implementation details that we need to hash out here, but first I want to make sure that we have agreement that this general direction is sound and FP can support it.

@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented Feb 7, 2018

friendly bump :)

@michael-oneill

This comment has been minimized.

Copy link

michael-oneill commented Feb 8, 2018

I like this proposal. So if no 1st party FP header does that mean 3rd parties cannot use CH.? If so like that a lot.
It would also be good from a privacy perspective if we could use a FP to control cookies being sent in 3rd party requests, i.e. not just in iframes but any resource request.

@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented Feb 8, 2018

So if no 1st party FP header does that mean 3rd parties cannot use CH.? If so like that a lot.

Correct, for any hint that has default policy of self.

It would also be good from a privacy perspective if we could use a FP to control cookies being sent in 3rd party requests, i.e. not just in iframes but any resource request.

That's a separate and unrelated request+thread to what we're discussing here — let's split this out into a separate discussion, if you want to pursue it.

@clelland

This comment has been minimized.

Copy link
Collaborator

clelland commented Feb 9, 2018

Yes, I think that this is reasonable -- I was thinking there were some interactions with the Accept-CH-Lifetime mechanism that might run counter to developer expectations with FP, but I don't think there's anything insurmountable there.

FP doesn't currently have any mechanism for allowing some features only in HTTP headers, and not in iframe attributes, but I'm sure there's room to define that if we come to the conclusion that it's required (I'm not convinced yet that it is)

@yoavweiss

This comment has been minimized.

Copy link
Contributor

yoavweiss commented Apr 10, 2018

Trying to think through the examples and looking at the ABNF, it seems that if an origin would want to delegate multiple CH headers to multiple other origins, we'd end up with a lot of repetition.
E.g. if example.com has 3 different image domains and wants all of DPR, Viewport-Width and Width, we'd end up with something like:
Feature-Policy: ch-dpr https://img1.example.com https://img2.example.com https://img3.example.com; ch-viewport-width https://img1.example.com https://img2.example.com https://img3.example.com; ch-width https://img1.example.com https://img2.example.com https://img3.example.com.

Is the above right? Or am I missing some less verbose way to do the same?

If there isn't one already, would be great to find a way to minimize the verbosity. (seems like something that can be applicable to other policies as well, not CH specific)

@clelland

This comment has been minimized.

Copy link
Collaborator

clelland commented Apr 10, 2018

CSP allows much richer wildcards than feature policy currently does -- If FP adopted some of the same parsing logic, then you could cut it down to

Feature-Policy: ch-dpr https://*.example.com; ch-viewport-width https://*.example.com;
  ch-width https://*.example.com

or (by more carefully choosing hostnames just for this purpose)

Feature-Policy: ch-dpr https://*.img.example.com; ch-viewport-width
  https://*.img.example.com; ch-width https://*.img.example.com

But that's not any better in the general case. I guess we could use some way to define named groups of origins, and then reference them in declarations. I wonder if something like that would fit with Origin Policy?

@clelland

This comment has been minimized.

Copy link
Collaborator

clelland commented Apr 10, 2018

Alternately, if we allowed multiple features in a single declaration, then you could specify all three features and all three origins in the same declaration, like

Feature-Policy: ch-dpr ch-viewport ch-width https://img1.example.com
  https://img2.example.com https://img3.example.com

That might be minimal, although it is definitely moving away from CSP in terms of parsing.

@yoavweiss

This comment has been minimized.

Copy link
Contributor

yoavweiss commented Apr 11, 2018

Or maybe even Feature-Policy: ch-dpr ch-viewport ch-width https://img*.example.com?

I understand we're reducing verbosity at the expense of more generic parsing, which also fits the CSP model, so not saying that's the obvious right choice, but might be worth while to put some thought into that. Maybe CSP can benefit from something similar as well.

@clelland

This comment has been minimized.

Copy link
Collaborator

clelland commented Apr 11, 2018

That's an interesting idea -- I know that CSP explicitly does not support wildcards in that position (and maybe for good reason; @mikewest would probably know)

I do kind of like the syntax of

token1 token2 token3 src1 src2 src3

as a shorthand for

token1 src1 src2 src3; token2 src1 src2 src3; token3 src1 src2 src3;

and I suspect you're right that CSP could probably adopt that as well. I should probably start another thread for potential parser changes.

@arturjanc

This comment has been minimized.

Copy link

arturjanc commented Apr 11, 2018

CSP deals with this by having a default-src which lets you define a source list which applies to most other directives. So something like ch-default *.example.com; ch-dpr img1.example.com might be the most similar in spirit to that approach.

@colinbendell

This comment has been minimized.

Copy link

colinbendell commented May 18, 2018

I would like to propose changing the default restriction for DPR, Viewport-Width and Width in the default policy. While DPR and Viewport-Width are certainly finger-printable on desktop, on mobile devices these values are generally a constant for a given mobile device. The DPR for a Pixel2 is the same for all Pixel2 hardware. Further, these values are already derivable with a User-Agent
databases. Therefore, I would propose that the default policy for mobile devices with DPR and Viewport-Width be relaxed.

Likewise, the 'Width' client hint is only present where the developer has added other annotations (<img sizes>). If the src or srcset point to a third party, then this implies acknowledgement of the Width to the third party. Therefore, I would propose that Width not be restricted by default.

To summarize, I would propose the following change for the default policy.
On Mobile:
Hint: DPR → FP: ch-dpr with default policy of *.
Hint: Width → FP: ch-width with default policy of *.
Hint: Viewport-Width → FP: ch-viewport-width with default policy of *.
Hint: Save-Data → FP: ch-save-data with default policy of *.
Hint: downlink → FP: ch-downlink with default policy of self.
Hint: device-memory → FP: ch-device-memory with default policy of self.

On Desktop:
Hint: DPR → FP: ch-dpr with default policy of self.
Hint: Width → FP: ch-width with default policy of *.
Hint: Viewport-Width → FP: ch-viewport-width with default policy of self.
Hint: Save-Data → FP: ch-save-data with default policy of *.
Hint: downlink → FP: ch-downlink with default policy of self.
Hint: device-memory → FP: ch-device-memory with default policy of self.

@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented May 19, 2018

@colinbendell I'd strongly prefer that we avoid branching policies based on form factor: we should enforce same privacy requirements for all devices, and I'm sure that we'll find edge cases and exceptions for every hint — e.g. some "mobile" devices allow split screen browsing, which affects Viewport-Width.

@yoavweiss

This comment has been minimized.

Copy link
Contributor

yoavweiss commented Jun 26, 2018

CSP deals with this by having a default-src which lets you define a source list which applies to most other directives. So something like ch-default *.example.com; ch-dpr img1.example.com might be the most similar in spirit to that approach.

That would mean that in my above example, it'll come down to:
Feature-Policy: ch-default https://img1.example.com https://img2.example.com https://img3.example.com.

I can live with that if it gets this shipped sooner. (by relying on existing constructs)

@eeeps

This comment has been minimized.

Copy link

eeeps commented Jul 9, 2018

If there's a ch-default feature identifier, it will probably become the most common way to set these policies. Using ch-default will have the (probably intended) effect of turning DPR/Width/Viewport-Width on, for the specified origins; it will also have the (probably unintended) effect of restricting Save-Data to a narrow allowlist, rather than its default *.

@igrigorik – why did you propose * as the default allowlist for ch-save-data? What would be lost if standard boilerplate ends up restricting that?

default-ch would similarly make deploying any new hints with a default policy of * much more difficult. Not sure how to measure or think about that potential cost...


(related thought)

If we go the route of allowing multiple feature-identifiers per declaration, e.g.,

Feature-Policy: ch-dpr ch-width 'self' https://third.party.host

Would it be possible to have those declarations enable the sending of those hints to those origins as well? e.g. this would be implied by the former:

Accept-CH: DPR, Width

This would be a different way to reduce repetition... by giving those enumerated values more of a job to do.

@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented Jul 14, 2018

why did you propose * as the default allowlist for ch-save-data? What would be lost if standard boilerplate ends up restricting that?

This was a (Chrome) policy decision, to encourage site owners to optimize for users that enable "use less data" option in their browser.

Would it be possible to have those declarations enable the sending of those hints to those origins as well? e.g. this would be implied by the former..

That is, effectively, the behavior we converged on. The 1P origin must use Accept-CH to advertise opt-in, and if it also provides FP specifying which 3P origins are allowed, then same hints are sent to said origins.

@eeeps

This comment has been minimized.

Copy link

eeeps commented Aug 30, 2018

I’m trying to work through how this should be spec’d, and I'm running up against a disconnect.

Integration with HTML enables and disables features within a document, based on the document’s own origin.

What we want to do here, is enable and disable features on each request, based on that request’s URL’s origin.

Will spec'ing this delegation require a new Feature Policy integration? With Fetch, maybe?

Alternatively, perhaps Content Security Policy is a better tool to do this delegation job?

  • Plus: CSP is designed around doing things based on request URLs, and is well-integrated with Fetch already.
  • Minus: those things are usually just blocking or allowing requests/responses, not, say, adding/stripping headers.

@clelland @igrigorik

@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented Sep 7, 2018

@eeeps hmm, good points. One gotcha I see here is that CSP is not a perfect match either..

Content Security Policy is a declarative policy that lets the authors (or server administrators) of a web application inform the client about the sources from which the application expects to load resources.

In this case, we're not controlling sources from which resources can be loaded, but aiming to control what outgoing headers requests are annotated with.

@mikewest any thoughts on if/how CSP could be a fit here?

@mikewest

This comment has been minimized.

Copy link
Member

mikewest commented Sep 7, 2018

I don't think adding new things to CSP is a good idea. It's already doing more than it ought to do because of past-me's poor design decisions.

I do think you'll need to integrate with Fetch one way or the other in order to figure out when to attach headers. Since you're doing that in the context of a given request, perhaps you can examine properties of the request's client in order to determine which hints ought to be applied?

That is: Feature Policy applies to the document, the document makes a request, Fetch calls an algorithm that looks at the document's policy to figure out what to do. That's what CSP does (in step 4 of https://fetch.spec.whatwg.org/#main-fetch which calls into https://w3c.github.io/webappsec-csp/#should-block-request).

@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented Sep 9, 2018

@mikewest we do have existing plumbing in Fetch (see step 7 in Fetching), where we differentiate between navigation and resource requests.

The discussion here is about defining a new mechanism where 1P is able to explicitly delegate delivery of hints (which and to which origin) to 3P origins. Previously, hints were delivered to all origins once 1P opted-in, but we're changing this behavior to: opt-in is for 1P origin only, and 1P must explicitly delegate to 3P's it wants the UA to deliver CH hints to.

Conceptually, FP is one way to tackle this use case: 1P uses FP to list origins and hints to which it delegates CH permission, similar to permission delegation. However, as @eeeps pointed out, there is a small mismatch here: other FP policies are document wide, whereas CH policy is all about origins — e.g. a 3P subresource loaded by top-level document should not get the hints unless 1P explicitly allowed it. Hence the thought experiment about CSP..

On other hand, CSP is not a perfect match either, since it controls sources from which resources can be loaded, not what data is sent to said origins. Conflating the two could get pretty confusing, and my hunch is that it would be even more so than FP.. WDYT?

@eeeps

This comment has been minimized.

Copy link

eeeps commented Sep 24, 2018

The above PRs represent a first stab at this. As this is my first time contributing to any of these specs (or using Bikeshed! at all!), I'm sure there are numerous issues. If and when those are able to be worked out, I see a few remaining issues to be tackled:

  1. Right now, Feature Policies can only be set on documents. If these changes all land and are implemented, authors still won't be able to attach/send Client Hints within Service Workers.
  2. Feature Policies still can’t be set in HTML, so sending Client Hints to third parties will be much harder than it used to be (and for many folks who only control HTML, using Client Hints will become impossible).
  3. As discussed above, in practice, setting feature policies for each Client Hint and origin is a bit verbose. This is the least of my concerns.

Lastly – I was basing these PRs on the language and functionality that’s already in the Fetch spec. So I didn't think very deeply about how these changes will affect the Accept-CH-Lifetime header/cache. @yoavweiss do you forsee any particular problems, in that regard?

@igrigorik

This comment has been minimized.

Copy link
Member

igrigorik commented Sep 29, 2018

@eeeps first off, big thank you for your help with this! Left a few comments on the PRs.

  • I think we need to move/merge the logic you proposed for CH spec directly into FP
  • We also need to define Accept-CH processing in HTML spec and then update Fetch to reference that
@Malvoz

This comment has been minimized.

Copy link
Contributor

Malvoz commented Oct 3, 2018

@clelland

With parameterized features, we could instead do:

Feature-Policy: client-hints 'self' https://cdn.example.com(save-data, viewport-width, width)

equating to:

Feature-Policy: client-hints 'self'(device, 
                                    downlink,
                                    dpr,
                                    save-data,
                                    viewport-width,
                                    width)

                               https://cdn.example.com(save-data, viewport-width, width)

correct? :)

@mikewest mikewest referenced this issue Nov 14, 2018

Closed

`sec-metadata` #280

3 of 5 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment