Re: Request-Response Binding Issues in httpbis-message-signatures-15

The goals here are different, I think.

Signing the individual elements in a request is a way for the sender of the response to say “this is what I’m responding to”. One of those elements could be the incoming signature, since it’s just another header field like any other bit of information.

Where we’ve gone wrong is the assumption that by signing the signature you’re reliably and transitively signing the components covered by that signature — assuming you validate the signature, of course. In the right circumstances, as shown in the attack in this thread, that assumption is not true. Including the Signature-Input header as an additional covered component as “signature-input”;req would be another way to mitigate the attack in question, since the attacker would also have to match all the message parameters of Alice, including Alice’s key ID.

So if a server really does need to communicate that a specific content-type was used, it would just sign “content-type”;req and the original HTTP client that sent the request would need to be able to look up what content type they used in order to validate the response signature. The client doesn’t even have to send a signed message to use this feature, that’s just what our example used. 

With the “@signature” proposal, we’re saying something else. We’re saying “I’m going to include everything that went into your original signature base directly in my own signature base”. It’s not a hash (which could have its own collision problem), it’s the actual signature base, encoded in a way that makes it safe to include. So the signer and verifier both generate this signature base twice.

Basically, both my proposal and Dennis’s works like this, just with different triggers and inclusion mechanisms for the syntax:

Client: has request message MReq, signing key SignKeyC, verification key VerifyKeyS

Client: creates signature base BaseReq over message MReq

Client: creates signature SigReq from BaseReq using SignKeyC

Client: applies SigReq to message MReq, sends to server

Server: has request message MReq’ (possibly transformed from MReq), verification key VerifyKeyC, signing key SignKeyS

Server: creates signature base BaseReq’ over MReq’ — in a valid signature, BaseReq’ is exactly equal to BaseReq

Server: verifies signature SigReq over BaseReq’ using VerifyKeyC

Server: creates response message MRes to MReq’

Server: creates signature base BaseRes over message MRes, includes signature base BaseReq’ in BaseRes

Server: creates signature SigRes from BaseRes using SignKeyS

Server applies SigRes to MRes, sends response to client

Client: receives MRes’ (possibly transformed)

Client: sees that MRes’ signature SigRes includes a signature over components of MReq, particularly the full signature base of BaseReq

Client: creates signature base BaseRes’ over MRes’, including BaseReq generated from MReq — in a valid signature, BaseRes’ is exactly equal to BaseRes

Client: verifies signature SigRes against MRes’ using VerifyKeyS


Since the attacker won’t be able to create the same BaseReq for a sufficiently-covered different request message, then this process locks out the attacker and the client’s final verification fails because the client is using MReq but the MReq’ that the server used is based on a different message that doesn’t sign in the same way.

I think this process is fairly complex, but workable if you know what you’re getting in to — ie, using @signature, not just special-casing a specific header. A lot of the same properties can be achieved by just signing the individual parts of the request message, like including the signature input line to the target signature. I think the lift on each of those approaches is a bit different, and specific applications are going to be in a better position than the general-purpose draft to decide what needs to be covered. That said, I am fully in favor of giving developers accurate advice as to what properties the protocol gives them.

Dennis, please let me know if I’ve misrepresented anything here in the proposed solution, or if I’ve left any holes here.

Thanks,
 — Justin



> On Feb 9, 2023, at 2:10 AM, Watson Ladd <watsonbladd@gmail.com> wrote:
> 
> On Wed, Feb 8, 2023 at 12:35 PM Justin Richer <jricher@mit.edu> wrote:
>> <chop>
>> 
>> Furthermore, I think there’s an alternative approach which could fit the work of an extension: have a new derived component with the behavior listed in the proposal below. For argument sake let’s call this “@signature” and we can even have it take a parameter to select which signature of the target message is being called upon. The component value of this derived component would be the signature base of the target signature, re-generated and then encoded, perhaps as an sf-string item to escape newlines and quotes and other potentially problematic items, or even enforce use of the “bs” flag that would give us the encoding properties we’re after. That way there’s no special casing of the field, it’s a production rule of the derived component definition that’s either supported or not by the implementation — and any derived components that aren’t recognized cause an error and cessation of processing of the signature base. Annabelle and I discussed doing exactly this a while ago but rejected it because of the additional complexity it adds on top of just signing a header like any other header.
> 
> I'm not sure that @signature or the original proposal actually work
> for a different reason: How does the sender HTTP request knows which
> header fields affect the contents of the response and need to be
> signed? E.g. if content-type isn't signed in the request, but the
> server wants to associate response with request and the response
> varies with content type, we've got an issue. The safe thing would be
> to reject, but I don't know how practical that is. So I think the
> server needs to sign exactly the fields that affect its processing of
> the response extracting them one by one.
> 
> What if we explicitly added a Commitment option that works like a
> signature but just takes a hash for use in response signing? While
> this is impacted by my observation above, it it is just signing
> another header and is easily generated by the same code as produces a
> signature: instead of signing, hash. Might be a good chunk of text
> though.
> 
>> 
>> My proposal for addressing this issue is to take the following actions, none of which fundamentally change the existing draft’s processing:
>> 
>> - Update the examples and advice that talk about signing another signature value and instead have the examples sign more specific components instead of the signature (for the request-response binding) or additional rationale for the transitivity of the trust (in the reverse proxy example).
>> - Add a security consideration section that details this specific sig-over-sig attack, using Dennis’s well-reasoned example below.
>> - Expand the signature collision section with discussion of the kind of key-collision that makes this attack viable, and which has been studied in the referenced papers.
>> - Target the expanded “@signature” derived component, expanded above, as an extension to this draft with its own deeper discussion.
>> -  Bring this issue to the OpenID Foundation’s FAPI working group, which is where the original non-repudiation linking use case and requirement came from that lead to the addition of this function. I believe that community can offer some important insight into how this could be addressed.
> 
> I'm not familiar with FAPI, but I think this might be worth reaching
> out to academics/research groups capable of providing such analysis.
> 
> Sincerely,
> Watson Ladd
> --
> Astra mortemque praestare gradatim

Received on Thursday, 9 February 2023 17:20:50 UTC