Re: Cache control in trailers?

On Wed, Feb 03, 2021 at 02:00:23PM -0500, Ryan Sleevi wrote:
> > > I do have to agree with PHK here: this sort of merging is a state machine
> > > security nightmare, especially when thinking about the interaction with
> > > resources, caching, and the overall semantics of HTTP.
> >
> > For me, the main ennemy of trailers is the fact that they were considered
> > as the part of the same namespace as headers, which is what causes this
> > merging nightmare. But just like some fields are forbidden in trailers, we
> > could state that they ought to be ignored in headers (in case of merging
> > by an intermediary), and that's why I really do not want to see both use
> > the same name. Once you stick to this, there's no merging nightmare nor
> > security issue anymore: if the trailer is present, you know what to cache.
> > If it's absent (and being in the header is counted as absent), you don't
> > cache, period. It's only suboptimal. But suboptimality is what encourages
> > improvements in products. Breakage encourages fragmentation.
> >
> 
> I'm not sure this is entirely correct, in the example you describe below
> (and more response there). That is, I think you've described a scenario
> where they're still part of the same logical namespace, in practice, even
> if they're not meant to be.

They are by definition from the spec (see 7230#4.1.3 which even explains
how to merge them). What I mean is that by declaring the field's name and
its behavior in both parts we avoid any type of confusion for those who
don't know it and those who receive it molested.

> > Then there are those which choke on trailers because they stop after 0
> > CRLF,
> > they wouldn't cache either. What I like with this approach is that a
> > degraded
> > message cannot be restored later in the chain to become correct again. This
> > wouldn't be the case if using the same field name in both parts.
> >
> 
> From an implementation state machine complexity, this ends up still being
> messy. For example, in Chrome at least, we use a multi-process architecture
> such that there is a network process, a browser process, and a renderer
> process. The renderer process requests a resource from the network process,
> and that result is then sent back via IPC through a fixed-size circular
> buffer.

OK.

> Using your example, the header says "don't cache", the trailer says "do
> cache". Currently, Chrome's implementation uses the header-defined value to
> determine whether it needs to "tee" (in the *nix sense) the response to
> both the disk cache and the renderer process, or whether to send it
> straight through to the renderer process. In order to ensure backpressure
> is properly handled, if the disk IO of the cache activity slows, it
> naturally slows the transmission rate to the renderer process. In effect,
> we only have a fixed amount of memory in use by a resource at a time.

This makes sense.

> Using your example, the need to cache after the fact in the trailer
> wouldn't be possible, not without buffering the entire response either in
> memory or through some temporary file, in order to know what to do with the
> response.

But why ? I guess the vast majority of responses received by the browser
are currently cacheable, so how this one would make any difference ? The
condition to decide to tee would just differ, you'd tee if cache-control
says either "public, "private" or "trailers" for example, and never for
other responses like today. And when doing "trailers", you'd finally
decide to either commit the object at the end of the transfer or destroy
it.

Note that I'm not at all trying to infer how this works in details, and
I know we all have our technical limitations for historic reasons or
anthing, but I don't see how an object fetched with a conditional cache
statement would require more storage or disk bandwidth than one fetched
with an explicit or implicit cache statement. You could see it as a final
message saying "finally I'd prefer if you dropped what you've just fetched
from your cache" after a usual response.

> The same challenge applies in the inverse (where the header says
> cache and the trailer says don't cache),

We must not do this, because that would mean that non-compliant
intermediaries will be fooled.

> These aren't just "correctness" issues, but we view them as security
> relevant, because they can allow for things like denial of service (via
> resource exhaustion),

Here I don't see how, as you don't cache more than what you normally
receive. You just know at the end of the transfer if it's worth keeping
what you just stored in your cache.

> From a server security standpoint, a number of services
> draw security boundaries between "headers" (which should be
> trusted/controlled by server admin) and "bodies" (which can be controlled
> by hosted code/untrusted parties), and the introduction of "trailers" and
> any semantics would have to try to preserve/respect those assumptions.

This remains true. Everything is in the header, only the final verdict
may happen in the trailer. Don't see it as an order for doing something,
rather as an opportunity. The Cache-Control header would say "I consider
it's worth caching this object, but if I encounter any issue, I'll tell
you at the end so that you can freely drop it". The other trailer says
"Object assembly went well enough, according to what I announced in
cache-control, I consider it worth keeping it in your cache if you want".

If you fiddle with cache-control in the middle, that's enough to void the
trailer's effect, so headers clearly are the ones defining everything
related to the semantics here.

> That
> is, trailers that redefine the semantics of headers, even if separately
> named, would and could be abused, which equally makes trailer bits
> unexciting.

This is also why I don't want to use similar header names on the two sides.

> I agree it's better for intermediates to use the semantics you described.
> That feels similar to the set of mitigations adopted by HSTS/HPKP - namely,
> that they only accepted the 'first' occurrence of a header and relied on
> the headers not being mergeable, in order to try to preserve the separation
> I mentioned above. But it doesn't feel terribly exciting from an
> implementation perspective, and feels like it could easily lead to new
> classes of security bugs if generally available.

I hardly see which ones if the header defines the rules and the trailer
only provides an operational status to let the client act as it prefers.

Regards,
Willy

Received on Wednesday, 3 February 2021 19:50:53 UTC