- From: Dennis Jackson <ietf@dennis-jackson.uk>
- Date: Mon, 27 Mar 2023 14:34:56 +0900
- To: Justin Richer <jricher@mit.edu>, HTTP Working Group <ietf-http-wg@w3.org>
- Message-ID: <b52ce52a-c733-e75b-42e0-5e5ed91e2330@dennis-jackson.uk>
Hi Justin, You appear to have merged the PR as-is. As I detailed in my earlier message, the change doesn't address the attack. Have I misunderstood the handling of the header in your proposal or are you still considering the path forwards? Best, Dennis On 07/03/2023 00:29, Dennis Jackson wrote: > > Hi Justin, > > Sorry for taking a couple of days to get to this, but I don't think > the proposed changes do anything to fix the attack. > > In your changed example, you've included the `signature-input` field > from the request in the response, but this field doesn't include the > referenced field values from the request. The resulting signature base > of the responder lists the expansions as: > > ``` > signature-input";req;key="sig1": ("@method" "@authority" "@path" \ > "content-digest" "content-type" "content-length")\ > ``` > > As this doesn't capture the field values, the attacker can still > mutate the value of all of these fields from the request. The > responder's signature base needs to include either: > > * All of the request's field values directly; or > * Some equivalent binding, e.g. a hash of all the field values in > the request. > > If taking the first option, the signature base for the responder would > need to cover the actual value of each field, e.g.something like: > > ``` > "content-digest";req:sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ > aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: > > Let me know if I've misunderstood or if the example text for the > signature base wasn't updated correctly. As you can see this will be > quite verbose when repeated for every signed field in the request, > which is also why I favor defining a derived @signature field to do > this on behalf of the application. > > I have further feedback on capturing these changes as explicit MUST > requirements and some minor nits on your description of the attack, > the proxy example and the conflict between 7.3.1 and 7.3.7, but they > can wait until we've clarified this issue. > > Best, > Dennis > > On 03/03/2023 12:20, Justin Richer wrote: >> Hello everyone, >> >> I’ve created a pull request to address the issues Dennis raised in >> this thread: >> >> https://github.com/httpwg/http-extensions/pull/2452 >> >> I believe that this addresses the concerns by providing appropriate >> guidance, advice, and warnings. I would be interested in pursing a >> specialized “@signature” derived component as an extension, if there >> is interest in doing so. >> >> — Justin >> >>> On Feb 8, 2023, at 3:32 PM, Justin Richer <jricher@mit.edu> wrote: >>> >>> Hi Dennis, thanks for bringing this issue to the group’s attention >>> and for your thorough writeup of the details involved. >>> >>> I want to start by summarizing that I agree with the attack vector >>> being a problem, but I disagree with Dennis’s proposed means of >>> addressing it within this spec. >>> >>> First, it’s important to know that this attack relies on their being >>> a weakness in the underlying cryptographic primitive: namely, that a >>> signature collision can be forced between two different, unrelated >>> signatures bases. It doesn’t matter if the originator signs all >>> required components of the message or not because the attacker can >>> force the output of their signature to be a binary match against the >>> signer’s. >>> >>> Where I disagree with Dennis is the proposed mitigation. I do not >>> think it is reasonable to special-case the “signature” field in the >>> way that is proposed. While doing so would mitigate the specific >>> problem here, I believe there are several major problems with this: >>> >>> - Special casing a single field like this is bound to get missed by >>> implementations and libraries, and I fully believe handling of this >>> would be iffy at best. Saying that developers will always rely on a >>> full-blown library do handle signature generation is, I believe, not >>> realistic. While I think it’s preferable for there to be a >>> well-vetted and supported library dedicated to a core function, the >>> truth is that people out there are going be implementing this inline >>> in a lot of places because the signature base generation is >>> ultimately about deterministic string production and off-the-shelf >>> libraries can be used for the crypto primitives. >>> - Special casing the “signature" field begs the question of whether >>> other fields could be forged in a similar fashion (content-digest, >>> for instance?), causing related but slightly different attacks. >>> - Adding the original signature base in verbatim to the old >>> signature base is going to create a component value with newline >>> characters. These characters are explicitly disallowed in order to >>> prevent other forms of constructions attacks, where the attacker can >>> force in one value that looks like it’s terminating a previous value >>> to overwrite something else. >>> - The attack relies on weaknesses in underlying crypto, and this >>> spec /intentionally/ does not limit the kinds of digital signature >>> or MAC that can be used by an application. It’s entirely reasonable >>> that an application that requires the kind of non-repudiation link >>> that drove the addition of the request-response binding function >>> would also specify a signing algorithm, key properties, and any >>> additional parameters that prevent that kind of forgery as well. >>> - We can call out the problem case with sufficient detail to let >>> application developers decide what to do with their application of >>> HTTP Signatures. This spec was always intended to be somewhere in >>> the middle of an application stack, with applications defining the >>> properties they need in the end result. Adding this kind of >>> complexity feels like a pretty major foot-gun that would be better >>> addressed in a wholistic manner up the stack. >>> >>> >>> 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. >>> >>> 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. >>> >>> >>> Dennis has offered off-list to help work on whatever the final text >>> ends up being. >>> >>> >>> — Justin >>> >>>> On Feb 8, 2023, at 11:09 AM, Dennis Jackson >>>> <ietf@dennis-jackson.uk> wrote: >>>> >>>> Bringing some off-list discussions with the authors (and others) >>>> back to the list: >>>> >>>> *What's the tl;dr? >>>> * >>>> >>>> The Message Signatures draft has serious cryptographic issues which >>>> break the claimed properties around request-response binding and >>>> allow for practical attacks. Depending on the exact specifics of >>>> the application, attackers can forge responses without having >>>> access to the responder's signing key. These issues are very >>>> similar to previously known signature attacks on request-response >>>> protocols like WS-Security, the initial ACME certificate issuance >>>> protocol (as reported by Andrew Ayer) and SCION's DRKey protocol [1]. >>>> >>>> *Has this been fixed in -16? >>>> * >>>> >>>> No. >>>> >>>> *How do the attacks work? >>>> * >>>> >>>> An attacker can forge transcripts and violate the request-response >>>> binding, without having to break the signature scheme's >>>> cryptographic properties, compromise a signing key or change >>>> another party's public key. The attacker exploits properties which >>>> are common in widely used signature schemes: >>>> >>>> 1. The same signature can validate under two different public keys. >>>> 2. The same signature can be valid for two (or more) different >>>> messages. >>>> >>>> The Message Signatures drafts assumes that this is not the case, >>>> assuming that a signature can be verified only for a unique public >>>> key and a unique message. >>>> >>>> *What is the suggested fix?* >>>> >>>> The draft needs to be updated to ensure that whenever a signature >>>> is made over a signature, it is also made over the message >>>> (signature base) associated with the signed signature. This can be >>>> done either by having Message Signature libraries automatically >>>> include the signature base when including a signature or by >>>> insisting that users ensure all such fields from the request are >>>> manually listed in the new signature base. >>>> >>>> I strongly believe that IETF drafts should be secure by default and >>>> so this should be handled by the library. Leaving it to application >>>> developers is a recipe for mistakes and omissions. >>>> >>>> This means adjusting the text to add a new MUST requirement to >>>> include signature bases when processing signatures within signature >>>> components, fixing the examples and correcting the security >>>> considerations. >>>> >>>> *Can you sketch what a concrete attack looks like? * >>>> >>>> Here's a version of the example protocol in 2.4, where Alice a >>>> client is using a financial service from server Charlie. All the >>>> parties are using RSA Signatures (doesn't matter which variant). >>>> >>>> Alice -> Charlie: >>>> {"I want to pay Bob, what account details should I use?"} >>>> Signature { Content-Digest } := Sig1 >>>> >>>> Charlie -> Alice: >>>> {"Use Acc #: 123456, Ref #: 789} >>>> Signature { Content-Digest, Sig1 } := Sig2 >>>> >>>> After getting the response, Alice checks that Charlie's >>>> content-digest + Sig2 is valid and makes the payment to Bob's >>>> account details. >>>> >>>> Let's see how Mallory (the attacker) can steal Alice's money. >>>> Mallory is also a user of the system and has compromised Charlie's >>>> TLS key but NOT Charlie's Message Signature signing key. Firstly, >>>> Mallory intercepts Alice's TLS connection to Charlie and receives >>>> the message: >>>> >>>> Alice -> 'Charlie': >>>> {"I want to pay Bob, what account details should I use?"} >>>> Signature { Content-Digest } := Sig1 >>>> >>>> Based on Alice's signature sig1, Mallory then crafts a new public >>>> key PK_M with a special property: >>>> >>>> RSA-PKCS#1v1.5-Verify(PK_M, Sig1, "I want to pay Mallory, what >>>> account details should I use?") == True >>>> >>>> In English: PK_M is chosen such that Alice's signature Sig1 >>>> verifies under a different message for Mallory's key. This is NOT a >>>> forgery or a breakage of the signature scheme. This is in fact easy >>>> to do for the RSA-PKCS#1v1.5 signatures as used in things like TLS, >>>> see the academic papers I linked in the initial post. >>>> >>>> Mallory registers PK_M as her public key with Charlie and then >>>> starts a new request/response: >>>> >>>> Mallory -> Charlie: >>>> {"I want to pay Mallory, what account details should I use?"} >>>> Signature { Content-Digest} := Sig1 >>>> >>>> Charlie -> Mallory: >>>> {"Use Acc #: 666666, Ref #: 333} >>>> Signature {Content-Digest, Sig1} := Sig2 >>>> >>>> Mallory now delivers Charlie's response and signature Sig2 back to >>>> Alice, presenting it as a response to Alice's initial Request. >>>> Alice now sees: >>>> >>>> 'Charlie' -> Alice: >>>> {"Use Acc #: 666666, Ref #: 333} >>>> Signature {Content-Digest, Sig1} := Sig2 >>>> >>>> Alice check's the content digest and signature against Charlie's >>>> public key, both are correct. Alice transfers the money to account >>>> number #666666, thinking those are Bob's details and Mallory >>>> retires as a wealthy criminal. >>>> >>>> This violates the request-response binding claimed by the draft. >>>> >>>> >>>> -- >>>> >>>> Best, >>>> Dennis >>>> >>>> [1] https://dennis-jackson.uk/assets/pdfs/signatures.pdf >>>> >>>> On 02/02/2023 09:12, Dennis Jackson wrote: >>>> >>>>> I think there are some issues which lead to practical attacks in >>>>> the Request-Response Binding section of >>>>> httpbis-message-signatures-15 >>>>> <https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-15.html>. >>>>> I took a look through recent github issues and mailing list >>>>> discussions and I didn’t see anyone raise similar issues before, >>>>> but apologies if this is a duplicate. My understanding is based >>>>> purely on a read of the spec, I haven’t messed around with the >>>>> implementations. >>>>> *Problems* >>>>> httpbis-message-signatures-15 specifies a scheme for binding >>>>> requests and responses together (Sect 2.4 >>>>> <https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-15.html#name-request-response-signature->) >>>>> where the response includes components from the request which are >>>>> tagged with |req|. The example provided in the draft uses the >>>>> signature component from the request: >>>>> Request: >>>>> >>>>> {"Hello, World"} >>>>> >>>>> Signature-Input: sig1=("@method" "@authority" "@path" \ >>>>> >>>>> "content-digest" "content-length" "content-type")\ >>>>> >>>>> ;created=1618884475;keyid="test-key-rsa-pss" >>>>> >>>>> Signature: sig1=:LAH8BjcfcOcL... >>>>> >>>>> >>>>> >>>>> Response: >>>>> >>>>> {"Your call is important to us"} >>>>> >>>>> Signature-Input: reqres=("@status" "content-length" "content-type" \ >>>>> >>>>> "signature";req;key="sig1" "@authority";req "@method";req)\ >>>>> >>>>> ;created=1618884479;keyid="test-key-ecc-p256" >>>>> >>>>> Signature: reqres=:mh17P4TbYYB... >>>>> Creating a signature over a signature from another party is a >>>>> cryptographic mistake. This is because the security definition of >>>>> most common signature schemes does not cover the case where the >>>>> signer is malicious and so leaves the resulting behavior undefined. >>>>> Practically, this allows for a couple of different attacks. >>>>> Firstly, it means that when using RSA-PKCS / RSA-PSS signatures. >>>>> an attacker who witnesses the above request-response pair and can >>>>> register a new public key, can choose a public key such that sig1 >>>>> is valid for a different message [1]. For example, they could >>>>> choose {“Goodbye, World”} or any other string and then produce the >>>>> faked transcript: >>>>> Request: >>>>> >>>>> {"Goodbye, World"} >>>>> >>>>> Signature-Input: [...] keyid="attackerKey" >>>>> >>>>> Signature: sig1=:LAH8BjcfcOcL... (unchanged) >>>>> >>>>> >>>>> >>>>> Response (unchanged): >>>>> >>>>> {"Your call is important to us"} >>>>> >>>>> Signature-Input: [...] >>>>> >>>>> Signature: reqres=:mh17P4TbYYB... >>>>> The responder (or any third party) would verify this transcript >>>>> successfully. The responder’s signature validates because it is >>>>> unaltered as are its inputs. The request signature validates >>>>> because the attacker has chosen their public key such that it is >>>>> valid for the alternative message content which contains their >>>>> keyid and their own choice of message. This allows the attacker to >>>>> take a legitimate response from the server to a different user and >>>>> apply it to a request of their choice. >>>>> A similarly powerful attack is also possible without access to a >>>>> request-response pair from another party. The attacker registers a >>>>> malicious public key in advance, such as the all-zero Ed25519 key. >>>>> When using this public key, it is trivial to generate signatures >>>>> which validate for any message. The attacker engages in the >>>>> request-response protocol and stores the transcript. Later, the >>>>> attacker change their request to be whatever message they like and >>>>> the resulting transcript will still validate. Depending on the >>>>> application, this could allow them to equivocate, change their >>>>> vote, alter a timestamp, etc. >>>>> This type of attack is very similar to a previous one we found and >>>>> formally modeled in 2019 on WS-Security, which also used a >>>>> signature of a signature to bind request-response pairs. See the >>>>> discussion and protocol diagram in 5.2 of Seems Legit >>>>> <https://dennis-jackson.uk/assets/pdfs/signatures.pdf>. >>>>> In the Security Considerations section of the draft (Sect 7.3.1 >>>>> <https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-15.html#name-cryptography-and-signature->), >>>>> the existence of signature collisions is acknowledged but the >>>>> impact is largely dismissed and it is suggested the user only use >>>>> vetted public keys and signature algorithms in the draft’s >>>>> registry. However, at least half of the algorithms in the HTTP >>>>> Signature Algorithms Registry are known to suffer from these kinds >>>>> of problems [2] and I am unaware of any IETF standard which >>>>> specifies how to vet public keys to avoid these kinds of attacks. >>>>> The draft also suggests that the inclusion of the signature >>>>> parameters in the signature base may foil these types of attack >>>>> but I’m quite doubtful as many of the attacks allow for a >>>>> arbitrary choice of message content, meaning that the signature >>>>> parameters can be freely replaced by the attacker. >>>>> *Suggested Fixes* >>>>> The minimal fix would be to rework the example in 2.4 to avoid a >>>>> signature over a signature and the guidance in 7.3.1 adjusted. >>>>> This could be a simple as just highlighting that signing signature >>>>> components doesn’t bind the message contents and that if the >>>>> response signature is to include the request signature, it should >>>>> also include the request signature’s components directly. >>>>> However, I think the draft would be much improved if it gave users >>>>> a sensible default mechanism for how to use request-response >>>>> signing in a robust / secure way via a SHOULD recommendation. >>>>> Users could choose to apply request-response binding differently >>>>> but should only do so on the understanding that they need to do >>>>> their own security analysis and can’t rely on any included within >>>>> the draft. >>>>> A sensible default would be that all signed components of the >>>>> request should be included in the response signature, as well as >>>>> the request’s signature and signature components. This avoids any >>>>> surprising mismatches between what the requester and responder >>>>> were authenticating. This could be done either by explicitly >>>>> including every signed request field in the responder’s signature >>>>> components (increases message size, simplifies implementation) or >>>>> implicitly by defining special rules for interpreting a >>>>> “signature;req” component in a signature components(saves space, >>>>> adds small amount of implementation complexity). Specifically, the >>>>> implementation would replace "signature;req" by looking up the >>>>> signature component for the associated signature and process each >>>>> element of that, as well as the signature and associated public key. >>>>> I would lean towards the latter because its less sharp edges for >>>>> the user and I’m struggling to think of any situation in which you >>>>> would want to include a signature, but not the contents of a >>>>> signature. >>>>> *Further Reading* >>>>> >>>>> * In 2005, Pornin and Stern introduced some of the key concepts >>>>> around these kind of signature behaviors and show in Sect 2.1 >>>>> how to construct malicious RSA public keys with the necessary >>>>> properties. [PDF >>>>> <https://www.bolet.org/~pornin/2005-acns-pornin+stern.pdf>] [1] >>>>> >>>>> * In 2019, we looked at how how these types of attack impact >>>>> protocols in Seems Legit [PDF >>>>> <https://dennis-jackson.uk/assets/pdfs/signatures.pdf>] [2]. >>>>> Table 1 provides a summary of the different signature schemes >>>>> and their properties. Section 3 explores how the different >>>>> properties impact various simple request-response protocols. >>>>> Section 5 describes some practical real world attacks on >>>>> deployed protocols. >>>>> >>>>> * >>>>> In 2020, we looked at the case of Ed25519 specifically [PDF >>>>> <https://dennis-jackson.uk/assets/pdfs/ed25519.pdf>] and gave >>>>> a proof that with sufficient additional checks which are not >>>>> part of RFC 8032, these behaviors can be removed from Ed25519. >>>>> >>>>> Best, >>>>> Dennis >>>>> >>> >>
Received on Monday, 27 March 2023 05:35:27 UTC