¿Es esta una debilidad de la firma XML envuelta?

10

Puede haber un problema en muchas aplicaciones basadas en la verificación de firmas XML (siempre que no me equivoque, por supuesto).

Tengamos un mensaje XML simple con una firma XML envuelta:

<?xml version="1.0" encoding="UTF-8"?>
<message>

<msgenvelope id="SIGNED_DATA">some signed data</msgenvelope>

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
 <SignedInfo>
  ...
  <Reference URI="#SIGNED_DATA">
   ...
  </Reference>
 </SignedInfo>
 <SignatureValue>naUY...+xZbEA=</SignatureValue>
</Signature>
</message>

De acuerdo con este MSDN , debes verificar un documento XML de este tipo con la clase SignedXml :

SignedXml signedXml = new SignedXml(Doc); //Doc is my message
XmlNodeList nodeList = Doc.GetElementsByTagName("Signature");
signedXml.LoadXml((XmlElement)nodeList[0]);
bool ok = signedXml.CheckSignature(Key);
if (ok) {
    string signedData = Doc.SelectSingleNode("/message/msgenvelope").InnerText;
    //do something with the signed data
} else {
    //throw error or something
}

Creo que hay un problema: el método CheckSignature verifica si el valor de la firma es correcto, pero no si los datos firmados son realmente los datos que se espera que se firmen.

Un tipo malvado podría modificar el mensaje de esta manera:

<?xml version="1.0" encoding="UTF-8"?>
<message>

<msgenvelope id="anotherid">FAKE DATA!!</msgenvelope>

<evil_envelope>
 <msgenvelope id="SIGNED_DATA">some signed data</msgenvelope>
</evil_envelope>

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
 <SignedInfo>
  ...
  <Reference URI="#SIGNED_DATA">
   ...
  </Reference>
 </SignedInfo>
 <SignatureValue>naUY...+xZbEA=</SignatureValue>
</Signature>
</message>

Este mensaje se verifica correctamente, porque el elemento firmado y la firma siguen siendo los mismos. Sin embargo, la cadena de datos resultante contiene "FAKE DATA !!".

Hay varias maneras de evitar este ataque: usar la verificación de esquema contra XSD, verificar el atributo id del elemento confiable, etc. ¿Cuál es el enfoque recomendado para deshacerse de este riesgo? ¿Debería mejorarse el artículo de MSDN? ¿Hay alguna implementación de referencia que maneje este problema correctamente?

    
pregunta vojta 10.01.2016 - 14:32
fuente

4 respuestas

1

Como ya se dijo, esto es así por diseño. Probablemente el artículo de MSDN podría (o debería) ser más explícito al respecto. La forma en que extrajo los datos "firmados" es incorrecta. La documentación de XMLDSig es clara al respecto. Puede leer la sección 8.1 de este enlace enlace

  

Del mismo modo que un usuario solo debe firmar lo que "ve", las personas y   Mecanismo automatizado que confía en la validez de un documento transformado.   sobre la base de una firma válida debe operar sobre los datos que   fue transformada (incluida la canonicalización) y firmada, no la   Datos originales pre-transformados.

y

  

Tenga en cuenta que el uso de XML canónico [XML-C14N] garantiza que todos   Las entidades internas y los espacios de nombres XML se expanden dentro del contenido.   siendo firmado Todas las entidades son reemplazadas con sus definiciones y el   La forma canónica representa explícitamente el espacio de nombres que un elemento   de lo contrario heredaría. Aplicaciones que no canonizan XML   El contenido (especialmente el elemento SignedInfo) NO DEBE utilizar información interna   Las entidades y DEBERÍAN representar explícitamente el espacio de nombres dentro de la   contenido firmado, ya que no pueden confiar en la canonicalización para   haz esto por ellos Además, los usuarios preocupados por la integridad de la   Definiciones de tipo de elemento asociadas con la instancia XML que se está firmando.   puede desear también firmar esas definiciones (es decir, el esquema, DTD, o   Descripción del lenguaje natural asociada con el   espacio de nombres / identificador).

     

Segundo, un sobre que contiene información firmada no está asegurado por   la firma. Por ejemplo, cuando un sobre cifrado contiene un   firma, la firma no protege la autenticidad o   la integridad de los encabezados de sobres sin firmar ni su forma de texto cifrado,   solo asegura el texto sin formato realmente firmado.

Más detalles sobre cómo validar correctamente una firma se encuentran aquí: enlace

  

La entrada a la primera Transformada es el resultado de la desreferenciación de la   Atributo URI del elemento de referencia.

Uno solo debe confiar en el resultado de la desreferenciación de referencias de SignerInfo. Todos los demás datos no deben considerarse firmados y no deben presentarse al usuario.

PS: Sí, XMLDSig es muy complejo. Y las implementaciones pueden fácilmente equivocarse.

    
respondido por el CristianTM 20.01.2016 - 19:27
fuente
1

Para mí, esto no parece realmente un error del protocolo sino un uso incorrecto.

¡Obtienes una parte del XML usando Doc.SelectSingleNode("/message/msgenvelope") pero nunca comprobaste que "/message/msgenvelope" es en realidad la parte firmada del XML!

Debe tratar el XML más como un sistema de archivos con archivos diferentes y no como una entidad. Piense de esta manera: tiene tres archivos: a.exe , b.exe y secure.signature Usted marca secure.signature y le dice: El archivo a.exe fue firmado por Trusted Guy y no se modificó. Si luego ejecuta b.exe y cree que esto es seguro, no es un problema de seguridad de la firma.

¡Esto es básicamente lo que haces en el ejemplo! Usted pregunta si la Firma es válida y devuelve que es válida. Pero también indica <Reference URI="#SIGNED_DATA"> , lo que significa que la firma es válida exactamente para #SIGNED_DATA y nada más.

A continuación, solicita obtener la parte identificada por / message / msgenvelope y la trata como si tuviera una firma válida.

Pero / message / msgenvelope claramente no es lo mismo que #SIGNED_DATA . Podría identificar el mismo elemento, podría no identificarlo. Pero lo más seguro es que nunca hayas comprobado la firma de / message / msgenvelope explícitamente.

    
respondido por el Josef 20.01.2016 - 15:34
fuente
1

La firma XML es propensa a demasiados ataques. Esto se debe a la forma en que se define XML como formato de archivo y cómo se implementa mediante el análisis de marcos.

enlace

enlace

    
respondido por el Wolfram Kluge 20.01.2016 - 17:18
fuente
1

Agregaré esto aquí porque no he visto la respuesta específica para este caso. La firma XML y las tecnologías relacionadas se mejoraron significativamente en .NET 4.5. Se agregó el soporte para cualquier longitud de clave y para las firmas SHA256. He utilizado este código con éxito antes.

El problema principal es con la etiqueta de referencia de su firma:

Usted le dice explícitamente a la clase SignedXml que busque SOLO ese elemento. Para firmar todo el documento, el atributo Uri debe estar vacío. Aquí hay un ejemplo de un documento XML firmado SHA-256:

<License xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Id>243</Id>
    <CustId>4365</CustId>
    <CustName>Joe Blow</CustName>
    ...

    <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/2001/04/xmldsig-more#rsa-sha256" />
            <Reference URI="">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <DigestValue>gNyvSh639wV7wHa4UYGPG524pjQ8JZBgaHhEiAm541k=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>ntQaT+PMZIS6eke81Vu0uRy8JJDhDfPic5e9Er34tDm00oprQ4qAFVJ1reuXSt+GIf/8XZAV0vR9RLqbB6R5K26lfQc5FCUotLYYjAYexFxwFzJqFV2hrYjhNxYHnXZRs37wY9iVbZlrG7fmEvqg7uN5cb1/K5a3VTFPoZvcUYkswfbzgxmdMdFDdOJCLLLA5oQEI3E60G32FABTJi11Sn9vCSnyePEJdi8yhJCUU9897bD7t2vkoyfbl7Ud5UyEPXUuKDBuX1uIUlU1WatlvH4qghaeV/LfQk8RSP7wHrtrB6T281ko+1+CdebnjTg5FTjo8vwknBXgDK8CRSQVm6DxNf0zeE+IGOhGXFRMCfFOsS9/jnKLT0wMIIqxPMKBX5cXDTX/4udHw6hLEc9H9X/vQLCyTl76ew8gdpgtZZKt8T/Tms8GUrAcIqZYIsUO399LS17lPtOJ2rXlzhDZSjRdVzHnQmGOWxDMtRF9Jb6b13Gr9JuXtPOmrJTl9kCsr+Dv81/h1aCa6xuwIkJtKS2n233+E6zsuSXj/eQJH56lsOJq9ijyXPtRV8LPXkY1Dta5vBwV2EeBA2LAzVOqU6SmM0B99XMCV90PcRLw71OnpdmMs/iUBQNyzn3Awk68hcJy5H3StZD5kl41RObYHQLvVU8/U6bFuwUiY1MAizM=</SignatureValue>
    </Signature>
</License>

Este documento se firmó con este código:

public static XmlDocument SignXml(XmlDocument xmlDoc, RSACryptoServiceProvider rsaKey)
{
    try {
        SignedXml signedXml = new SignedXml(xmlDoc);
        signedXml.SigningKey = rsaKey;
        signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";

        Reference reference = new Reference();
        reference.Uri = "";
        reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
        reference.AddTransform(new XmlDsigExcC14NTransform());
        reference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";

        signedXml.AddReference(reference);
        signedXml.ComputeSignature();

        XmlElement xmlDigitalSignature = signedXml.GetXml();
        xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
        if (xmlDoc.FirstChild.GetType() == typeof(XmlDeclaration))
            xmlDoc.RemoveChild(xmlDoc.FirstChild);

        return xmlDoc;
    } catch (Exception ex) {
        xmlDoc = null;
    }
    return xmlDoc;
}

Al usar este método, todo el documento está firmado, no solo un elemento.

Puedes verificar la firma de esta manera. Cambiar cualquier byte en el archivo firmado invalidará la firma:

public static void VerifySignedXml(XmlDocument xmlDoc, RSACryptoServiceProvider rsaKey)
{
    SignedXml signedXml = new SignedXml(xmlDoc);
    XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature");

    if (nodeList.Count > 0) {
        signedXml.LoadXml((XmlElement)nodeList(0));
    } else {
        throw new Exception("Signed XML verification failed: No Signature was found in the document.");
    }

    if (!signedXml.CheckSignature(rsaKey)) {
        throw new Exception("Signed XML verification failed: Document signature did not match.");
    }
}

También tenga en cuenta que un documento puede tener varias firmas. Por ejemplo, podría mantener la firma que tiene ahí arriba, específicamente para ese elemento, y luego agregar otra firma para todo el documento. El código anterior asume el primer elemento de firma encontrado.

    
respondido por el Drunken Code Monkey 23.01.2017 - 04:58
fuente

Lea otras preguntas en las etiquetas