Requirements on Signature-Input
The worst aspect of the HTTP signatures work is that it requires careful profiling to ensure that you get the properties that you are expecting.
In this case, it seems like the properties you want are achieved if you only sign Identity-Digest, but I'd want some analysis of that to be sure. I can't think of a reason that wouldn't suffice, but "thinking hard" is not the benchmark I'd use for this.
If that is all that is needed, then the algorithm should check that Identity-Digest is listed.
Otherwise, the signature could be of nothing in particular, rendering it useless.
Hey Martin! I agree that the question of what to sign is important. Discussions with folks planning on deploying this mechanism (@ddworken, @yoavweiss, @punkeel, etc) have generally landed on unencoded-digest being essential, @path being useful in many cases, and other headers (like content-type) being potentially desirable to support.
Currently, the profile requires that unencoded-digest (née identity-digest) be present as the only signed component, and forces strict serialization of that header's content (see Step 3.2 of the algorithm you linked). Chromium's prototype also supports @path, and I'll poke at the spec to add that in (probably along with the other derived components, as they're quite straightforward to support).
I think we'll likely want to expand to allow other components beyond that core, as briefly discussed in https://github.com/WICG/signature-based-sri/issues/40#issuecomment-2575526852, but I don't see any version of this scheme that wouldn't require unencoded-digest (and enforce that the content matches that header).
Oh dear, I completely missed that because I didn't realize that the field had been renamed. (The new name is worse.) I'd appreciate it if it only checked that unencoded-digest is present.
Oh dear, I completely missed that because I didn't realize that the field had been renamed. (The new name is worse.)
Others thought the previous name was worse. 🤷 Names are hard.
I'd appreciate it if it only checked that
unencoded-digestis present.
Does this mean you'd prefer that we not extend the set of acceptable components to include either other headers or the derived components from RFC9421? @path in particular seems to mitigate potential misuse of signed content in unexpected contexts, and seems valuable. I'm less enthusiastic about the other derived components, but they're simple enough to implement... Is there an advantage to restricting ourselves to the single header?
Sorry, maybe I should have been clearer: If the signature covers more stuff then I would prefer this to work. I don't think that we need to cover more for this use case, but I'd like to reserve that option. That means making this slightly more accepting now, to help sites that generate this manage that future risk.
OK, let me try again. I don't like the idea that @path is involved, but I don't see a way around it. The problem with @path is that you need to establish some sort of expectation, which means that you just eliminated 302 and friends as tools that a server can use. Unless you want to allow signing of redirects...
Sorry, maybe I should have been clearer: If the signature covers more stuff then I would prefer this to work. I don't think that we need to cover more for this use case, but I'd like to reserve that option. That means making this slightly more accepting now, to help sites that generate this manage that future risk.
I think you're suggesting that we should require unencoded-digest's presence in the list of components, but also accept other components that a server may wish to include. I agree with you that unencoded-digest is really the only thing that's necessary, and I also agree that providing developers with more flexibility has some value.
The simplest way to provide that flexibility would be to allow specification of any arbitrary header, and to simply pass its content through to the serialization of the signature base that we end up signing over. I've been a little wary of doing that, as it introduces more points of potential confusion (e.g. formatting differences in which the signing backend works with content-type: text/html;charset=utf-8 and the frontend library produces text/html; charset=utf-8), but it's easy enough to support in the client.
I'm a little concerned about leaving developers without clear guidance, though: this dovetails with the request for guidance at the bottom of https://github.com/WICG/signature-based-sri/issues/40#issuecomment-2574638487.
OK, let me try again. I don't like the idea that
@pathis involved, but I don't see a way around it. The problem with@pathis that you need to establish some sort of expectation, which means that you just eliminated 302 and friends as tools that a server can use. Unless you want to allow signing of redirects...
This is a really good point, thanks for bringing it up. I'd been assuming that @path would refer to the initial URL that was requested (which would prevent redirects along with substitutions of the response body), but now it's not actually clear to me whether that's right. Is "the request target" the request's URL or current URL in Fetch's parlance?
If the latter, I need to think more about reasonable behavior for each intermediate hop. Signing them isn't crazy, though we'd likely want to require the content-location header? Hrm.
Breaking this out into https://github.com/WICG/signature-based-sri/issues/45.
I think that the best and clearest guidance is to include the minimal set of things under signature. Once we all work out what that is, of course.
That we might later decide to include something more is the only thing we protect by allowing for arbitrary other fields. We could even advise against exercising that option, precisely because these signatures are likely to be so fragile.
That we might later decide to include something more is the only thing we protect by allowing for arbitrary other fields. We could even advise against exercising that option, precisely because these signatures are likely to be so fragile.
I think allowing for arbitrary other fields is the same as deciding to include something more. That is, if we accept something like ("unencoded-digest";sf "content-type") as a component list, I think we'd need to serialize both headers into the signature base, as:
"unencoded-digest";sf: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
"content-type": text/html; charset=utf-8
"@signature-params": ("unencoded-digest";sf "content-type");keyid="JrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=";tag="sri"
If we didn't include that additional header in the signature base, we'd have a hard time introducing it in the future, as older clients would (presumably?) generate a signature base including only known components, along the lines of:
"unencoded-digest";sf: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
"@signature-params": ("unencoded-digest";sf);keyid="JrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=";tag="sri"
In this case, the expected signature would change based on the client version, which seems difficult for servers to deal with.
I'm confused then. Are you using HTTP signing or not? If you are using it, then that spec will deal with the mechanics of validating signatures. The goal of this spec should be to ensure that Signature-Input contains whatever is necessary to meet the security goal (as the primary way of ensuring that the signature itself covers what is necessary). In that case, so what if it lists "x-not-a-header" as long as the signature validates at the other end?
The real challenge from upgrading clients isn't that, but the expectations they have. If a newer client expects "x-not-a-header" to be signed because $reasons, then signatures won't be accepted if servers don't sign that. Of course that is a problem we'd need to address, but that's something that we could address with a new Sec-Stuff-That-Absolutely-Needs-To-Signed-Or-The-Signature-Wont-Be-Accepted field in a request. At that point, yes, we'd probably want to include that sort of negotiation in the spec today in anticipation of needing that ability later. It could be added later, but you'd need that to be taken up by most servers before you could start adding new stuff to that list.
But that presumes that the servers are only signing for this one purpose. If there were multiple reasons that a server signed responses, then it might find that it needs to generate multiple signatures if one of them was so heavily locked down.
Consider that maybe we introduce a signed-sri-v2 that required the inclusion of more fields under signature (@path or a new nonce seem like reasonable candidates). Then, there is a version of the design that can be answered with one signature for both versions of SRI if the old one is a little more flexible.
Are you using HTTP signing or not? If you are using it, then that spec will deal with the mechanics of validating signatures.
As you noted elsewhere, that spec relies on profiling to define reasonable behavior for specific use cases. Here, we're more or less talking about the first two bullets of https://www.rfc-editor.org/rfc/rfc9421.html#section-1.4 to define the expected/required components and how we serialize them.
Consistency in serialization is the thing I'm hung up on here: it is easy to say that we accept arbitrary headers as components. I'm not entirely convinced it is as easy to get clients and servers to agree on how those arbitrary headers would be serialized, given the steps between signing and delivery. I don't think there's substantial technical risk in doing so, though; in my mind, the risk is entirely around developer confusion about what exactly constitutes the signature base.
Perhaps I should simply trust developers to not be confused? And implementers to have good error messages?
so what if it lists "x-not-a-header"
In this specific case, we'd fail to produce a signature base (per https://www.rfc-editor.org/rfc/rfc9421.html#section-2.5-7.2.2.5.2.6).
Then, there is a version of the design that can be answered with one signature for both versions of SRI if the old one is a little more flexible.
The current draft produces this flexibility by ignoring things it doesn't understand. That is, rather than accepting unknown components or parameters, it throws its hands up, says "This doesn't match the type="sri" profile.", and moves on to the next member in the dictionary. Servers can append multiple signatures with different sets of components and parameters, confident that clients that understand the old thing will ignore the new thing.
This is more or less the discussion we're having in #38, though focused specifically on parameters. It sounds like you're more in @ddworken and @punkeel's camp than mine, which is a reasonable signal that I'm just wrong. :)
We've landed on being lax with both parameters and components; if Message Signatures supports it, we should too. Based on folks experimental experience, that doesn't seem like it's going to be a deployment problem.