whatwg / fetch

Fetch Standard
https://fetch.spec.whatwg.org/
Other
2.11k stars 330 forks source link

Allow connection reuse for request without credentials when TLS client auth is not in use #341

Open annevk opened 8 years ago

annevk commented 8 years ago

The connection separation we have today is the result of TLS client auth which is a property of the connection, rather than the request.

The argument has been made that we should simply tag connections with respect to whether TLS client auth is used. If it is, a request without credentials cannot use that connection.

If it is not, that connection should be up for reuse by both requests with credentials and without.

We might have to cater for connections being made as a result of a request without credentials. If you later do a request with credentials, it might not be able to reuse that connection since that would prevent TLS client auth? (Do we know whether the server tried to use TLS client auth even if the client doesn't want it, then we might be able to optimize this even more.)

sleevi commented 8 years ago

NTLM and Kerberos are also connection-level auth methods, and Microsoft has expressed repeated interest in exploring a TLS-CCA 'like' HTTP method. So the need for socket independence is not just limited to TLS-CCA.

annevk commented 7 years ago

Can the client detect that NTLM and Kerberos are used?

As for TLS, perhaps we can let the server explicitly opt-out of these connection-sharing-preventing difficulties through an extension? That way CDNs can share connections for fonts and HTML resources.

sleevi commented 7 years ago

@annevk Can you clarify what you mean about client? Do you mean JS in the page or the receiving server?

And can you clarify whether you're talking TLS extension, fetch extension, or something else?

I do wonder if these suggestions are perhaps thinking about it inverted, in part, because we discussed these pools in context with @igrigorik as part of Resource Hints, and the logic for separation was to ensure that a non-credentialed request is not sent over a credentialed connection. I think the suggestion that was technically sound (just complex to implement) was to treat the pools as a 'common' pool for purposes of preconnect, and then assign them to 'credentialed' or 'non-credentialed' based on both how the connection was established (e.g. if it apriori sent credentials) and upon the disposition of the first request received over the connection.

Your remark about CDNs sharing connections for fonts and HTML resources makes me think the priority of constituencies is wrong - we don't share uncredentialed requests with credentialed requests because our privacy team does not want these to be linked (ignoring all the other ways that they can already be linked). So I don't think we would consider allowing a way for the server to say to send non-credentialed requests over credentialed connections, because that puts the server over the user.

annevk commented 7 years ago

By client I mean the browser engine. And the extension I was thinking of would be to TLS, though open to other suggestions.

With regards to priority of constituencies, I guess I would only argue for sharing if the same document was responsible for both requests. That's the case that worries folks. Where images from the CDN go over one connection, and fonts from the CDN go over another, just because of different defaults.

sleevi commented 7 years ago

@annevk Right, I can understand why for same-origin, non-credentialed loads, this is not ideal. If I understand your proposal correctly, the idea is that it would be safe to send same-origin, non-credentialed loads on the same underlying transport iff that transport did not bear ambient authority?

If we implemented that, my thought on the risks would be:

annevk commented 7 years ago

What I meant was an example.com document loading cdn.example resources, both with (images) and without (fonts) credentials.

I understand better now that this gets harder though if you involve multiple browsing contexts. How does that work today with 3p cookie blocking? If I load example.com in one tab, and another tab that is not example.com loads an image from example.com. Will those use separate connections if 3p cookie blocking is enabled? What if you two tabs that are not example.com loading an image from example.com?

sleevi commented 7 years ago

@annevk OK, so you're talking about H/2 coalescing, not same-origin resources, just to confirm?

The story of 3P cookie blocking is... complicated... and I suspect @mikewest can speak more to it. My most recent examination of the code was that when a request is identified as a 3P one for which cookies should block, then it (effectively) ends up as an uncredentialed request. As a consequence, it goes to our 'uncredentialed' pool for dispatch over the network. However, this is complicated by some of our renderer and memory cache behaviours, so I'm going to narrowly focus on the "Resource wasn't cached" scenario (in memory or on disk) for these examples:

annevk commented 7 years ago

I wasn't necessarily even talking about H/2 connection coalescing. More whether that example could use two rather than three connections and whether that would leak more somehow. But one or two or three is also interesting to consider of course.

As for tracker.example, I don't quite understand the connection reuse for the non-credentialed socket pool. It seems you could track users than across origin visits by carefully keeping track of the connection or maybe TLS session identifiers (assuming you're embedded often enough). I guess the tradeoff is that it's only for a session and not as long as a cookie.

sleevi commented 7 years ago

@annevk Yup. I'm not defending it as a good policy, if only because I'm not sure I agree with it given http://www.chromium.org/Home/chromium-security/client-identification-mechanisms

Basically, any request that explicitly opts to not include cookies goes over a connection guaranteed to have never sent cookies (modulo any bugs) or authentication information, while any connection that 'could' or 'has' cookies or authentication information goes over a different connection.

mikewest commented 7 years ago

Sorry I missed @sleevi's ping earlier. I'm willing to believe that we're making the wrong tradeoff here, and I think there's some justification to considering the implicit correlation of socket connections outside the scope of "credentials" explicitly sent along with requests. There's a bit of a grey area here, since we consider connection-level concepts like channel ID, token binding, TLS session information, etc. to be fairly explicit cookie-like things, but it's possible we're erring too far on the side of caution.

@battre and @msramek from Chrome's privacy team might have more informed opinions.

jakearchibald commented 7 years ago

@sleevi

Because the (current) connection is credentialed, since it loaded example.com, then if the H/2 connection also asserts origin identity for cdn.example.com, the request will be dispatched over the current connection.

Does this involve a DNS lookup for cdn.example.com to ensure it points to the same IP?

sleevi commented 7 years ago

@jakearchibald Depends on whether it's the first connection or not :) Fetch covers the answer to that (which is yes) =)

annevk commented 7 years ago

I finally started a discussion with other folks at Mozilla: https://groups.google.com/d/topic/mozilla.dev.tech.network/glqron0mRko/discussion. Updates from the Google side still appreciated of course. I wonder if @johnwilander or @hober could shed a light on Apple's thoughts here, and maybe @travisleithead on those of Microsoft?

annevk commented 7 years ago

Firefox might change what it does here: https://bugzilla.mozilla.org/show_bug.cgi?id=1363284.

mikewest commented 7 years ago

I've pointed our privacy folks at that bug and thread, and asked them to comment. Thanks for following up on this, @annevk!

yoavweiss commented 7 years ago

Just to comment a bit on the pain that this would solve: 1) No need for crossorigin attributes on <link rel=preconnect> 2) Enable the use of H2 pushed no-credentials resources (currently often pushed on the "wrong" H2 connection) 3) Enable improved priority handling of no-credentials resources, preventing their contention with higher-priority, credentialed resources.

While 1 & 2 can potentially be solved by (significantly) changing the implementations' handling of connection pools as well as the H2 push cache, 3 seems inherent to the use of multiple connections. Such bandwidth contention is already something I see often, and it will become more common as more resources (e.g. ES6 modules) are added as no-credentials.

annevk commented 7 years ago

The one thing I have not seen addressed anywhere yet is whether multiple connections still have advantages over a single connection as long as TCP is used. It seems that there would be less head-of-line blocking, or is the overhead of an additional connection really high somehow?

annevk commented 7 years ago

It also seems that #325 will lead us straight back to more authenticated connections. Which seems problematic?

igrigorik commented 7 years ago

The one thing I have not seen addressed anywhere yet is whether multiple connections still have advantages over a single connection as long as TCP is used. It seems that there would be less head-of-line blocking, or is the overhead of an additional connection really high somehow?

FWIW, I think this is an orthogonal discussion and shouldn't be a factor in this context.

achristensen07 commented 7 years ago

We reuse connections basically whenever we consider it advantageous to do so. If someone were to observe our networking stack, they might conclude that connections are dropped seemingly randomly. In reality, complicated heuristics are used to determine when to keep a connection open based on how long it's been unused, how many of various resources we have left, etc. It is unfortunate that this leaks some information across domains. Work is also currently being done to leak less such information. We should work towards a better solution in the long term, but it will be especially hard without compromising performance too much.

cramforce commented 7 years ago
screen shot 2017-08-14 at 4 34 24 pm

See screenshot for the real-world impact of Chrome's current behavior. The font downloads in rows 8-11 are delayed by that 1 second connection setup (simulated 3G).

Drawaes commented 7 years ago

My nagging thought here is, with the coming of TLS 1.3, will export material be available to clients in some programmable fashion? If so would this allow some "other" code that is reusing the connection to gain access to material that is potentially used to key/protect secret data? Would it basically make the "connection based" export key material "public" to anything running in the context?

pmeenan commented 7 years ago

I know we're not explicitly factoring H2 into this discussion but the other problem it causes is that the resources can no longer be prioritized against each other using H2 priorities. The credentialed and anonymous requests end up competing with each other for bandwidth unless the browser holds requests back (even in the case where connections are coalesced and the site is all served from a single origin).

jeisinger commented 7 years ago

IIRC the reason why we introduced this behavior is because we consider part of the connection state (ChannelID) a cookie, and wanted to ensure that blocking cookies (in the broad sense as implemented in chrome) also blocks those.

Using two socket pools for credentialed and uncredentialed requests was a trade-off between teaching the network stack about per-site cookie settings and privacy requirements.

I assume that the measurements cited here are done in a browser without cookie blocking configured, so maybe we should revisit this decision. @sleevi maybe we should move that discussion to a crbug, as this is really more about chrome's implementation than the actual spec in the end?

sleevi commented 7 years ago

It doesn't just apply to Channel ID though and wasn't just about that (as mentioned in past discussions) - and it's covered in the spec at present, so this seems like an appropriate place to discuss? That is, our problems don't just go away (for Chrome) if Channel ID didn't exist, and the privacy concerns repeatedly overruled the performance concerns.

annevk commented 3 years ago

I wonder if the introduction of network partitioning has changed the equation here somewhat as to whether connections still need to be keyed by credentials as well.

cc @MattMenke2

MattMenke2 commented 3 years ago

Seems like that would be doable for TLS client auth, but gets very weird in the HTTP auth + socket limits case, so I'd rather not go in this direction. Eg, suppose you've labeled on socket as credentials due to an NTLM challenge, and then you're at your socket pool limit for uncredentialed sockets, and then you get (uncredentialed socket max + 1) credentialed requests...do you hold back one of your uncredentialed sockets, since you don't want to upgrade them all? Socket assigned just gets very weird and complicated, and you now have a notion of sockets that are similar-ish to each other, and can be somewhat used interchangeably with respect to each other, but not quite.

MattMenke2 commented 3 years ago

Oh, sorry - I was reading the original proposal. Just getting rid of segregating credentialed sockets... it's a big enough change (yet still pretty invisible), that I'd be reluctant to change it out from under developers who may be relying on the current behavior.

yoavweiss commented 5 months ago

Getting back to this after 7 years.. ⏳ My comments regarding the benefits of changing the current spec are still largely valid, other than (2) given that server push is no longer a practical consideration. In short, a single connection improves things compared to two connections as it requires only a single connection establishment process, doesn't require two separate slow-starts, and can avoid contention on bandwidth.

@MattMenke2 - can you elaborate on how developers may be relying on current behavior?

In terms of benefits, I can see this improving some very common scenarios:

MattMenke2 commented 5 months ago

It's been 3 years since I made that comment, but I think that comment can probably be ignored. I believe I was concerned about enterprises distinguishing between connections that were instantly AUTHed on first request and those that would never be AUTHed, with not many cases in between, but I'm now skeptical the division in the current world any anywhere near accurate.

MattMenke2 commented 5 months ago

I guess the cookie separation may be easier to detect and hard-code assumptions on than the AUTH one, so perhaps I was more concerned about that case. Anyhow, consider my concern retracted.

davidben commented 5 months ago

There are some subtleties here to be careful around. It is possible for a connection to start life without requesting TLS client auth, and then after an HTTP request, suddenly request it. At that point, the connection has to choose which side of the divide it lives on. Should still be doable, but something to be wary of.

Now, in Chromium, we already just tear down the connection in that case and start it over from scratch (it's easier and this case is not a priority for optimizations), so we'd actually have an easier time with this, but I doubt everyone does this. (Though Chromium also has a long-standing bug where we do TLS client auth with uncredentialed requests anyway. No one's prioritized fixing that sadly. 😞)

But this also demonstrates that the exact implementation and behavior will vary a bit based on how the rest of the browser's net stack works. For example, we set a connection's client certificate disposition (send this cert, send no cert, tear down connection and ask) when the connection is first created. If the connection never sends CertificateRequest, this field ends up being a no-op. Probably the simplest implementation for Chromium would be to partition based on this disposition and not whether a CertificateRequest actually happened, as the latter would cause a connection to move buckets over its lifetime.

And then if draft-ietf-httpbis-http2-secondary-certs ever happens, that'll add another dimension to the mix. But hopefully any HTTP-layer thing we do can be controlled on a per-request basis (the USE_CERTIFICATE frame in that draft) and we don't need to rely on connection partitioning to do it.

LPardue commented 3 months ago

I just hit this issue with a site that uses HTTP/3 for loading a mixture of assets over "www.example.com" and "static.example.com" and echo Yoav's comment in https://github.com/whatwg/fetch/issues/341#issuecomment-2133649736 that having to have two connections here is unfortunate