http-extensions icon indicating copy to clipboard operation
http-extensions copied to clipboard

Signature Context

Open yaronf opened this issue 3 years ago • 12 comments

[This came up at the IETF-113 session, credit to Jonathan Hoyland]

A signature context mitigates oracle attacks, where a sender can be made to sign data that’s partly controlled by the attacker, and the signed string is then used for unexpected purposes, either within the same protocol or outside it.

  • The context includes the higher level protocol that utilizes the signature mechanism, as well as other information such as sender and receiver identities.
  • Signature context is used in TLS 1.3 (see RFC 8446, Sec. 4.4.3, “context string”) as well as the similar typ parameter in JWTs.
  • The simplest way to add context to Message Signatures is as an additional parameter.
("@target-uri" "@authority" "date" "cache-control")\
  ;keyid="test-key-rsa-pss";alg="rsa-pss-sha512";\
  created=1618884475;expires=1618884775;context="GNAP/1"
  • We suggest that the context value should NOT be defined by this draft, but it should be mandatory to send, and mandatory to validate upon receipt. This prevents cross-protocol attacks but assumes that the higher level protocol is able to protect itself e.g. by ensuring that the receiver’s identity is included in the signed components.
  • The context is fully defined by the higher level protocol, and no IANA registry is established for it.

cc: @jricher

yaronf avatar May 26 '22 09:05 yaronf

Thanks for writing this up. I want to make sure I understand the request for addition:

  • Define a signature parameter "context" as a string, whose content is outside the scope of this spec
  • Define the use for this field aligned with the above
  • Add a security considerations on the use of this in outside protocols

I think this is a relatively light weight add, and I am ok with those pieces.

Where I don't think I agree is the "mandatory to send and interpret" portion, if we're not going to define anything to put into the field. In this case is becomes just another nonce value, right? I can see a lot of confusion in people trying to apply this without clear guidance. I would be fine with this field being recommended to send by the signer and mandatory to interpret by the verifier if it's present, and applications can make the field required and even have required values, like the example above.

jricher avatar May 26 '22 15:05 jricher

Yes, you have it right.

This is analogous to the JWT typ claim whose goal is to prevent cross-protocol (or really, cross-usage) attacks. So making this mandatory is important. I think mandating verification is nice in theory but I'm not sure it would be effective in practice.

Instead, we could give guidance that makes it trivial to use, like in my example: include the protocol's name and possibly version. Just doing that would narrow the "blast radius" of cross-protocol attacks significantly.

A "nonce" (number used once) is a well defined term. This is not a nonce.

The text on typ is weaker than I would like, but it's still a SHOULD for new types of JWTs. Here we have an opportunity to get it right from day one.

yaronf avatar May 26 '22 18:05 yaronf

I appreciate the position, but what if the application is literally just "I am signing this message"? What would we put in that mandatory field that isn't either a random value or just "http"?

jricher avatar May 26 '22 18:05 jricher

Then put the application/service name there, instead of a protocol name.

yaronf avatar May 26 '22 18:05 yaronf

What's the application or service name, then? I don't think it's always as well defined as you're assuming it to be in practice.

jricher avatar May 26 '22 18:05 jricher

It's guidance, not interoperable normative text. Whether people use "myapp" or "myapp/1" or even "myfile.c" doesn't matter all that much. Basically anything is better than a missing context.

yaronf avatar May 27 '22 09:05 yaronf

If it's not interoperable normative text, then it has no business being a MUST here.

jricher avatar Jun 01 '22 21:06 jricher

Existence and verification of the parameter are both normative. Its content is not.

yaronf avatar Jun 01 '22 23:06 yaronf

@yaronf Could you provide an example attack scenario that cannot be mitigated by signing additional semantically relevant message components, and is mitigated by the addition of a context parameter?

The JWT typ claim is necessary because often there is nothing in a JWT that ties it to a particular message. This is a feature of JWTs, as it allows things like reusing JWT-formatted access tokens across multiple requests. I'm not familiar enough with TLS internals to say whether the same concept applies to the TLS "context string" parameter.

In contrast, reuse across messages is not a use case for HTTP Message Signatures. If a signature is reusable across two semantically different messages, that would suggest to me that the signature is not covering the right message components. So cross-protocol attacks where the signature is presented as a message signature for a different message are mitigated by signing the semantically relevant components. (e.g., repl-digest, @target-uri or parts thereof, etc.)

Attacks where the signature is presented as some other type of signature (i.e., not an HTTP message signature) are partially mitigated by the fact that the signature base includes the signature parameters, which is likely to distinguish it from content that may be signed in other contexts. For example, the format of the signature base means that an HTTP message signature cannot be passed off as a signature for a JWS. If the overall format and the @signature-params footer is not already enough for this, I don't see how adding an additional parameter would help. If an attacker can mold the signature input in another context to match the HTTP message signature format as it is, it seems unlikely to me that adding context="SomeOtherContext" to the end would help.

richanna avatar Jul 28 '22 19:07 richanna

Hi @richanna, I'll start with your last paragraph. I agree what we have now is only partial mitigation. This kind of attacks is never fully mitigated, because you need to make assumptions on what implementations of a different, unknown protocol would do. But in general IMHO a unique prefix is a much better way to distinguish between signatures, because something further down in the signature base may be ignored by verifiers. Concretely, having a mandatory httpsign\n as the first line of the signature base would be a much better mitigation.

Now back to the context: again, we are talking about two different protocols, each of which may have both spec and implementation errors. Of course if both protocols are defined and implemented perfectly, the right fields would be covered and validated, and would also define unique semantics. Life is not perfect - people are lazy and will sign too few fields - and a context protects some protocols/implementations from others.

yaronf avatar Jul 29 '22 15:07 yaronf

Concretely, having a mandatory httpsign\n as the first line of the signature base would be a much better mitigation.

I'm not sure I buy this. If a non-HTTPSig consumer of signed content can be tricked into accepting "@authority": app.example\n... as a valid message then I don't see why we should assume it would be any harder to get it to accept httpsign\n"@authority": app.example\n...

people are lazy and will sign too few fields

Okay, can we walk through the failure modes?

  1. An API that exposes multiple operations on different paths could fail to require callers to sign the target URI's path. In order for context to help, it would have to be set to a unique value per operation, e.g., "ExampleApp.doExampleThing". That seems like way more work than just signing the right things.
  2. An app could fail to require all of an operation's inputs to be signed. In this case, the only way context helps is if it is unique per request, e.g., a hash of all the operation inputs. This is definitely way more work than just signing the right things.
  3. Two apps could fail to require the request audience (e.g., the target URI authority) to be signed, allowing signatures to be replayed across them. Here context only helps if it is set to something that is app specific. That rules out the use of standardized context values based on protocol (e.g., "context"="GNAP/0.1") or intent (e.g., "context"="proof_of_possession"). And if context is being set to something app-specific, why not just sign the app-specific thing that is already in the request?

Are there other failure modes that I'm not considering, where context would prove more useful?

So far this leaves me feeling that context is more likely to encourage bad habits and give implementers a false sense of security than it is to address any actual security risks.

richanna avatar Jul 29 '22 20:07 richanna

I do not see how having a fixed signature prefix will solve any security issues with signature base generation. All the same problems with bad implementations occur internally in the signature base, the only difference is that you've got MAYBE a good first line -- and as @richanna mentioned this is not stopping people from doing different bad things like having newlines in the content identifier.

We can add a security or implementation considerations section on newlines in the signature base.

Even then, I am failing to see how this prefix idea is related to the signature context concept.

And as @richanna said, apps failing to sign enough is an issue for the app. Having a signature context isn't going to magically get that necessary content signed, nor will it make a badly written verifier reject unsigned required content.

Again, I'm fine with adding context as an optional parameter for applications that want to make use of it for signaling, but I don't think it solves things in the way that is being presented here.

jricher avatar Aug 01 '22 20:08 jricher