Skip to content

Commit

Permalink
Merge pull request #2 from overleaf/tm-normalization-patch
Browse files Browse the repository at this point in the history
Patch validateXmlSignatureForCert to attempt alternate normalization
  • Loading branch information
thomas- authored Jun 18, 2021
2 parents 683f767 + ba9be37 commit fa6c931
Showing 1 changed file with 36 additions and 28 deletions.
64 changes: 36 additions & 28 deletions src/node-saml/xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,35 +72,43 @@ export const validateXmlSignatureForCert = (
currentNode: Element
): boolean => {
const sig = new xmlCrypto.SignedXml();
sig.keyInfoProvider = {
file: "",
getKeyInfo: () => "<X509Data></X509Data>",
getKey: () => Buffer.from(certPem),
};
const signatureStr = normalizeNewlines(signature.toString());
sig.loadSignature(signatureStr);
// We expect each signature to contain exactly one reference to the top level of the xml we
// are validating, so if we see anything else, reject.
if (sig.references.length != 1) return false;
const refUri = sig.references[0].uri!;
const refId = refUri[0] === "#" ? refUri.substring(1) : refUri;
// If we can't find the reference at the top level, reject
const idAttribute = currentNode.getAttribute("ID") ? "ID" : "Id";
if (currentNode.getAttribute(idAttribute) != refId) return false;
// If we find any extra referenced nodes, reject. (xml-crypto only verifies one digest, so
// multiple candidate references is bad news)
const totalReferencedNodes = xpath.selectElements(
currentNode.ownerDocument,
"//*[@" + idAttribute + "='" + refId + "']"
);

if (totalReferencedNodes.length > 1) {
return false;
const originalXml = fullXml; // save original xml for restoring before second attempt
for(const overrideNormalization of [false, true]) {
fullXml = originalXml;
sig.keyInfoProvider = {
file: "",
getKeyInfo: () => "<X509Data></X509Data>",
getKey: () => Buffer.from(certPem),
};
const signatureStr = overrideNormalization ? signature.toString() : normalizeNewlines(signature.toString());
sig.loadSignature(signatureStr);
// We expect each signature to contain exactly one reference to the top level of the xml we
// are validating, so if we see anything else, reject.
if (sig.references.length != 1) return false;
const refUri = sig.references[0].uri!;
const refId = refUri[0] === "#" ? refUri.substring(1) : refUri;
// If we can't find the reference at the top level, reject
const idAttribute = currentNode.getAttribute("ID") ? "ID" : "Id";
if (currentNode.getAttribute(idAttribute) != refId) return false;
// If we find any extra referenced nodes, reject. (xml-crypto only verifies one digest, so
// multiple candidate references is bad news)
const totalReferencedNodes = xpath.selectElements(
currentNode.ownerDocument,
"//*[@" + idAttribute + "='" + refId + "']"
);

if (totalReferencedNodes.length > 1) {
return false;
}
// normalize XML to replace XML-encoded carriage returns with actual carriage returns
fullXml = normalizeXml(fullXml);
if(!overrideNormalization) {

This comment has been minimized.

Copy link
@mans0954

mans0954 Jun 18, 2021

fullXml = overrideNormalization ? fullXml : normalizeNewlines(fullXml); would be more consistent with line 83 above.

fullXml = normalizeNewlines(fullXml);
}
const isValid = sig.checkSignature(fullXml);
if(isValid) { return isValid; }
}
// normalize XML to replace XML-encoded carriage returns with actual carriage returns
fullXml = normalizeXml(fullXml);
fullXml = normalizeNewlines(fullXml);
return sig.checkSignature(fullXml);
return false; // maintain behavior of returning false if not valid
};

interface XmlSignatureLocation {
Expand Down

0 comments on commit fa6c931

Please sign in to comment.