Last week I showed a peculiar XML Signature that validates even though the containing document was changed. The reason is that the signature lacks References. Before explaining what’s wrong with the signature – and with the validation code, we’ll have a look at how XML Signatures work.
XML DSig Primer
XML in general is a powerful beast, with so many options available that it quickly gets really complex. The XML Digital Signatures standard is no exception to that. The extra features complexity of XML DSig compared to other signature standard is that one or more different blocks of data can be signed by the same signature block. That data can be the containing XML Document, part of an XML document or some other resource such as a web page. In this post we’ll only look at signing resources in the document containing the signature.
Consider a simple XML document.
<Document> <Colour ID = "yellow"> <Item>Sun</Item> <Item>Lemon</Item> </Colour> <Colour ID = "green"> <Item>Grass</Item> <Item>Cucumber</Item> </Colour> <Colour ID = "black"> <Item>Hackerz</Item> </Colour> </Document> |
Adding a signature to it can be done in two ways:
- An Enveloped Signature is a signature that is inserted into the data that is signed.
- An Enveloping Signature is a signature that is sitting side-by-side with the data that is signed.
Either way, a signature contains one or more references that identifies the element signed and a hash of the element’s contents. I’ve added a signature two the above element, containing two references.
The first reference has URI="yellow"
. This is an enveloped signature as the signature element is stored within the data that has been signed. The actual signature is excluded when calculating the hash.
The second reference has URI="green"
. This is an enveloping signature. It signs an element that is side-by-side by the signature.
Although this document contains a signature, which is valid, only part of the document is protected. The black section can be altered without the signature detecting it or new elements can be added as siblings to the <colour>
elements. This is what makes XML signature so powerful dangerous. It is not enough to validate an XML signature. The references must be checked too, to ensure that it’s the right data that has been signed. And that brings us to the answer to the XML signature in my previous post. That signature contains no references, so it doesn’t sign any data at all! Creating such a signature is normally not allowed in the .NET framework. To create it I used an old unit test that uses shims to avoid the check for references.
Always Check References and the Key
Did I mention that the references of an XML signature must always be checked? I hope so, because that is the most common mistake of XML signature validation. It is so common, that just about every well known SAML2 implementation was hacked through it a few years ago (OWASP has an overview slide deck about it).
The second thing besides checking the references (I did mention that, didn’t I?) is that the key must be checked. The most simple overloaed ofSignedXml.CheckSignature()
with no arguments just checks that the data is signed by a certificate that is trusted by operating system. The signature in my previous post will validate because I signed it with a certificate of mine issued by a trusted root. To be sure that the data actually came from the real sender, the information about the key must be checked. Or even better, the expected key should be supplied as an argument to SignedXml.CheckSignature()
to bypass the entire certificate storage.
The Complete Signature
In the image I excluded some parts of the XML signature for brevity. Here is the complete one, that can be validated. Please note that it is signed by an RSA Key, with the key values included in the signature. The only thing proven by this signature is that the creator of the signature is a holder of the private key. But there is no identity information on the holder of the key attached. So the signature is actually quite worthless.
<Document> <Colour ID="yellow"> <Item>Sun</Item> <Item>Lemon</Item> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> <Reference URI="#yellow"> <Transforms> <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> <DigestValue>sRhgxVLVLVNQwFV+DvjIgbcIoeU=</DigestValue> </Reference> <Reference URI="#green"> <Transforms> <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> <DigestValue>WfbY1xs4jWohgPF/eboGDahJqXM=</DigestValue> </Reference> </SignedInfo> <SignatureValue>sJQG9uukmyMXT42WJ+lMSCQLehGXamikavXw0Ty0nrTf+laQbrsorTv84vgW253VQ6VEdeIJ4AYyks0olgusEEq8x2Cm43XtnEJCcYe9n1+XDeloil60EOahJOyZp9dFORmPrEVnanR10LQWmHTjMN1itnRsSHwtzKrs57fhTPc=</SignatureValue> <KeyInfo> <KeyValue> <RSAKeyValue> <Modulus>srhYx87VpBrsftyteXe0FJIIPlGukuGxRHtjcQPwrttxEXkfqlZKahzgyeWlCQoMTFmvAqXZAB7eSVQyo6V/fEGtMXZ31q8ORY6q+G/u1L7XTJ18zVO/pkP7j+++QuMivoiPCJAsmqC5NS71IjJ6h5YTy677UVOcR215ukjq10E=</Modulus> <Exponent>AQAB</Exponent> </RSAKeyValue> </KeyValue> </KeyInfo> </Signature> </Colour> <Colour ID="green"> <Item>Grass</Item> <Item>Cucumber</Item> </Colour> <Colour ID="black"> <Item>Hackerz</Item> </Colour> </Document> |
Another good reason to consider using JOSE / JWT :)