Skip to content
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

Define the scope of feature policy #252

Open
clelland opened this issue Nov 21, 2018 · 13 comments
Open

Define the scope of feature policy #252

clelland opened this issue Nov 21, 2018 · 13 comments

Comments

@clelland
Copy link
Collaborator

After reading some recent comments (#248, #249. #166), I think it makes sense to start a discussion about what exactly feature policy is, and what it can and should do.

Originally, I intended the spec to be completely feature-agnostic, just defining the structure of policies and the semantics of combining them in documents and frames. (With a small carve-out in the spec to define specific behaviour around legacy attributes like allowfullscreen) There's an introduction, but it probably doesn't do a great job of explaining exactly why we need FP, and what kinds of problems it can be used to solve. Different people come away with different ideas about what it could be useful for. I think that having some examples of the very different kinds of features that FP can be used for will be a useful starting point for discussion.

All of this is my opinion, certainly, but It's based on the thinking that the Chrome team has done over the last two years, as well as what we've actually prototyped or shipped.

At its core, FP is about allowing site authors to control what features are available in their own pages, and to declare which of those features they want to delegate to specific frames. The goal is to provide a single unified mechanism for site authors, that works the same way for all features, rather than defining very different (or worse, slightly different) techniques for each feature in isolation.

Note that this is different than the user granting a particular use of a feature (permissions) or the browser itself deciding to turn features on and off heuristically (interventions), though there are certainly some conceptual overlaps. Those both exist outside of HTTP transport and HTML markup: Feature policy is a tool for site authors to declare something about the structure of their pages: which features it wants to avoid, and which frames it trusts.

Given this, I think that FP is applicable to these broad categories of behaviour:

Powerful features

Many features are considered extremely useful for documents on the web, but there is fear that they would be abused if allowed in every frame, given the way that developers often incorporate content from unknown third-parties (ads, specifically, but there are other cases as well. Mash-up sites using iframes, or even portals, can combine documents from essentially random sites into a single page). For many of these features, the response has been to allow them for top-level documents only, and block them in subframes.

Using feature policy for these (with a default allowlist of 'self') effectively accomplishes the same thing -- access is restricted to the top-level document, and its same-origin children -- while also allowing site authors to explicitly delegate access to trusted frames in a consistent way. The idea is that cooperating frames from different origins already have most of the tools they need to get around any top-level-only restrictions, using postMessage to either transfer data, or as a control channel from child to parent. Using FP means that these hacks aren't necessary.

Examples:

  • Fullscreen
  • PaymentRequest
  • WebAuthn
  • WebXR
  • Autoplaying video
  • Wake lock
  • Battery status

Delegating permissions

Closely related to the first category is the idea of delegating access to permission-gated APIs. Previously, any frame on a page could request access to the user's connected devices, personal information, or (in the case of notifications) attention. This is problematic for (at least) two reasons: Users have trouble understanding such requests from random domains, and often don't understand how to tell a request from a malicious advertisement apart from a legitimate request from a subframe which is part of the functionality of the main page; and second, the site author has no way to embed arbitrary content without the possibility that any frame could ask the user for access to these APIs.

Chrome is now using feature policy for many of these APIs, and requiring the top-level page to explicitly delegate access to trusted frames. This automatically blocks any other content from even requesting permission from the user; any attempts are immediately rejected.

Examples:

  • User media (camera, microphone, speaker)
  • WebMidi
  • Notifications
  • Sensor access (geolocation, accelerometer, gyroscope, etc)
  • WebBluetooth
  • Persistent storage

Sandbox features

Iframe sandboxing blocks a number of features which aren't normally blocked in subframes (they're not that dangerous), but which authors may want to restrict for particularly untrusted content. Using feature policy, we can make these into features with a default allowlist of * (so available everywhere by default) but give authors the ability to disable them as needed. (We haven't done this yet, but I've started exploring the idea in this doc.)

Examples:

  • Popups
  • Top-frame navigation
  • Orientation lock
  • Pointer lock
  • Form submission
  • Modals
  • Presentation mode
  • Scripting

Undesirable behaviour

Finally, there is a class of APIs and behaviours which are already shipped and available everywhere, and widely used, which we would like to discourage, and maybe eventually deprecate. There are slow, synchronous, oudated and wish-we-never-shipped-it-but-it-was-1995-at-the-time APIs like document.write, and there are performance-killing antipatterns like loading huge images into tiny thumbnail-sized containers. We can't remove these, since they are so widespread,

Site authors can do their best to avoid these APIs and patterns, but even very diligent, well-funded sites can have their pages' performance degraded by third-party content. Feature policy gives authors a tool to enforce best practises on their site, including on all content they embed. (This can come at the cost of potentially breaking some content, but I hope that reporting can be used to warn authors when things do break in the wild) @jpchase presented at Chrome Dev Summit last week on using FP to improve web development, focussing on this kind of feature.

For these sorts of features, there is a real benefit to using the Feature-Policy HTTP header, since that acts on the document which it accompanies, rather than on child frames. Any of these can be turned off for the entire page by using an allowlist of 'none' in the header, and this is necessarily inherited by all child frames.

Examples:

  • Synchronous XHR
  • document.write
  • Cookie access
  • Unsized media
  • Oversized images
  • Layout-inducing animations

That, I think covers all of the not-completely crazy ideas (if not specifically each of them, then at least the broad categories of problems that we can solve). There may be more that I've forgotten, and there are almost certainly more that haven't been thought of yet.

@bzbarsky
Copy link

Fwiw, it's not clear what the target audience of this discussion issue is. I came across it by accident. None of the people who raised concerns were cced...

That said, the fundamental problem I am having is that feature policy sort of pretends to be a generic string-to-boolean store but it's a very opinionated store in terms of propagation semantics. Most simply: propagation to subframes but not to auxiliary browsing contexts.

This makes it an acceptable fit for things that are somehow connected to "the url in the url bar" (i.e. the user-visible site identity; powerful features and permission delegation fit in that bucket) but a poor fit for anything else. It's an extremely poor fit for things to do with sandboxing, verging into "dangerous to use" territory because of the false sense of security you might get. This became very clear once we started doing security reviews of our implementation in Gecko and of the spec.

The fact that the "undesirable behavior" bucket propagates across origins in ways that can affect control flow was brought up as a serious security concern as well...

Basically, it feels like a system designed around one set of use cases was then stretched to cover quite different use cases, and it's not being a great fit there.

@bzbarsky
Copy link

@martinthomson

@clelland
Copy link
Collaborator Author

Sorry for not cc'ing -- I thought that GitHub linking the issues would just take care of that.

(I'm also not aware of who else from Mozilla was involved in those discussions, besides @martinthomson)

Sandboxing is an area that I would really like to get right. I think that we should figure out how to properly handle auxiliary browsing contexts. It's not hard to follow the sandbox model, except that I'd really like to do something sane to make noopener be the way to detach from your opener's policy, but "allow-popups-to-escape-sandbox" complicates that. @domenic had some ideas in that space.

Having the "default *" features affect other origins, and possibly change control flow, is definitely a discussion that should be had -- for features that we'd one day want to deprecate completely, like sync-xhr, it seems to me to be a useful way to move forward. document.write, .cookie, and .domain are in a very similar place. There are certainly concerns for each of them, and we've discussed some of this at length inside Chrome, but it would be good to have a public discussion about all of the issues.

(Also inviting @ojanvafai, @mikewest, @annevk to the discussion)

@clelland
Copy link
Collaborator Author

BTW, I agree completely that with the current state, where auxiliary browsing contexts get a default policy, implementing sandbox features cannot happen. I just re-read and realized that that's not at all clear from what I wrote.

@martinthomson
Copy link
Member

This view @bzbarsky articulates is critical to what this thing is good for. I think that what is here works very well for permissions - because it allows us to make the attribution of requests to users very clear. As mentioned, this is very good at attributing stuff to the top-level context. However, that suggests a narrower scope than you are contemplating.

Don't try to expand the domain of applicability for this. If it isn't suited to be a security feature, then don't force it. Addressing your categories in no particular order:

  • Permissions are a good fit for this. Our UX folks really want to be able to ensure that the origin making a permissions request is only ever the one shown in the URL bar. This gives us that and it's a valuable addition to the platform.

  • Powerful features are probably in scope. I'd argue that most are the sort of things that need permission anyway. If you take that view, then most of these make sense in the same way as permissions. Most of these are - or at least should be - disabled in nested contexts by default, so having a way to delegate the capability is sensible.

  • Interventions, like disabling synchronous XHR, make no sense to me. That's an entirely different thing and a poor fit for this spec. If you wanted to have a discussion about what signals a page might send that would cause these crufty APIs to be disabled, that would look very different to this API. I don't want to say DOCTYPE, but that's the level of signal I think is appropriate; this is perhaps the only way in which I see an HTTP header field being useful. I realize that we'll continue to add to this pile of cruft and so need a good story for how to opt into disabling more over time, but I don't see any value in having that posture imposed on nested contexts.

  • I was somewhat split on sandboxing. The question of nested vs. auxiliary is probably most informative here though. If the intent is to prevent an auxiliary context from navigating this one, then noopener seems better, for nested frames, I can see why all of these apply, but that suggests improvements to the existing sandboxing, not this API.

So I'd say that while this looks like a hammer that might hit a number of nails, some of those nails are probably screws and should be left for other tools.

@annevk
Copy link
Member

annevk commented Nov 28, 2018

If we want popups (i.e., auxiliary browsing contexts) to be included, and I still think we should as this is too attractive to not use as a security feature of sorts, I think the "browsing context group" concept (see whatwg/html#4198 (comment)) will help. rel=noopener would create a new browsing context group, so restrictions would not apply, but anything else would have restrictions. (But yeah, I think there's also some work to be done here to better explain the existing sandboxing system.)

@clelland
Copy link
Collaborator Author

Thanks, @martinthomson, I appreciate the feedback, and the insight there.

Interventions (although that word implies to me that it's an unpredictable browser heuristic, rather than something under control of the developer) are definitely a mixed bag. We (Chrome) are experimenting with using feature policy for things like 'oversized images', which is much more of a behaviour change than an actual 'feature', at least as web developers would think about it.

On the other hand, things like sync-xhr, document.write, cookies, etc, are not new to this spec -- they were part of the initial design, I believe; FP was hoped to be a mechanism for slowly deprecating these from the platform. (At one point, someone brought up the idea of a new DOCTYPE, which would do nothing but set a different default policy, disabling features which could then be opted back-in)

The two reasons I am strongly in favour of ensuring that these are adhered to in nested contexts are

  • Consistency within FP itself -- making it simple for developers to reason about how features behave, and being able to provide the promise that policies are never loosened in subframes
  • Ability for browsers to make optimization decisions about a whole page, based on the policy at the top-level.

Maybe these aren't compelling enough to insist that these features fit within the FP framework; maybe there are ways we can make it a more natural fit (or change the nesting behaviour somehow)

@martinthomson
Copy link
Member

I don't personally find the motivation strong enough here. That it was in scope for the initial design is an interesting piece of information, but it looks like the design has evolved to the point that it's not the best fit for that requirement.

Per-document optimization is a better approach for stuff we don't like any more and want to have go away. That might not be as big a performance win for browsers, but it keeps the surprises to a minimum. At least with permission-gated features, we have a well-established understanding that failure is possible. Finding that document.all doesn't work might cause surprising problems.

@ojanvafai
Copy link
Collaborator

The application to nested contexts allows first parties to enforce constraints on third parties. The sync-XHR case is a good example. The primary developer requests we've had for this have been around ensuring that ads they embed don't do sync-XHRs. I agree that it will be surprising for the ad, but for the first party it allows them to ensure a better user experience, which seems like a net win for users that in turn creates incentive for ads to stop using sync-XHR. Is that use case not compelling?

@bzbarsky
Copy link

One problem is that not everything in iframes is ads, and in particular that malicious sites can frame other sites.

Yes, said sites should probably consider sending X-Frame-Options but not everyone does...

I agree that the ad use case is worth considering. To what extent can the ads be trusted to opt in to being good citizens? I assume "not at all"?

@martinthomson
Copy link
Member

martinthomson commented Nov 30, 2018

Is that use case not compelling?

Not to me, no.

(Edited to clarify what I was responding to.)

@clelland
Copy link
Collaborator Author

The per-document use case is a big one. I'd been pretty firm on the idea that every feature needs to be disabled for entire subtrees, but maybe that only makes sense for permissions and powerful features.

I would disagree that everything needs to be per-document -- sandboxing itself shows that we can restrict features, even ones like modals and scripting itself, which can have large effects on the third-party page -- but yes, we need to be really careful around this. Our approach so far has been to try not introduce any new failure modes for existing APIs. SyncXHR, for instance, fails with a network error in a way that devs should be handling already.

doc.{write,cookie,domain,all}, synchronous scripts, etc. probably need to be handled differently. I've been thinking along the lines of what CSP is doing with embedded enforcement: Some features would be really valuable to impose on third-party code, but maybe we need a way for the third party to choose to accept the restrictions, or else reject being embedded.

I think that what is clear now is that we need to have different types of features -- in a way, we already do, they're distinguished by the default allowlist, but maybe we need to pivot there and think of the default allowlist as more of an "inheritance strategy", which is hopefully a more capable, more flexible concept.

aarongable pushed a commit to chromium/chromium that referenced this issue Feb 19, 2019
This CL introduces the "inherited opener feature policies". This
includes the logic to propagate feature policy states from a browsing
context to the auxiliary browsing contexts.

As the first step (and hidden behind flag) all the feature policies
will be inherited by the auxiliary browsing context. The only exception
is when the original context is sandboxed but allows popups to escape
sandbox.

The inheritance model will be fine tuned in further work. Firstly, not
all features might follow this "sandbox-like" inheritance model. Also
possibly through introducing a new Feature Policy (that replicates
'allow-popups-to-escape-sandbox') and special casing "rel='noopener'"
there will be exit doors for the open contexts to *not* inherit the
policies.

These issues are currently publicly being tracked here:

w3c/webappsec-permissions-policy#264
w3c/webappsec-permissions-policy#252
w3c/webappsec-permissions-policy#259

Bug: 774620
Change-Id: Ic0b5ab8155c2e5d786bc51d3f9c3a601f7e4d8e9
Reviewed-on: https://chromium-review.googlesource.com/c/1384992
Reviewed-by: Ehsan Karamad <[email protected]>
Reviewed-by: Mike West <[email protected]>
Reviewed-by: Ian Clelland <[email protected]>
Reviewed-by: Nasko Oskov <[email protected]>
Commit-Queue: Ehsan Karamad <[email protected]>
Cr-Commit-Position: refs/heads/master@{#633452}
@clelland
Copy link
Collaborator Author

I just opened #282 with a more concrete proposal for inheritance strategies; I'd like to see if that's a useful direction to head in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants