Si tratta di una debolezza della firma XML avvolta?

10

Potrebbe esserci un problema in molte applicazioni basate sulla verifica della firma XML (a condizione che non sbagli, ovviamente).

Abbiamo un semplice messaggio XML con una firma XML con involucro:

<?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>

In base a questo articolo MSDN , devi verificare tale documento XML con la classe 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
}

Penso che ci sia un problema: il metodo CheckSignature verifica se il valore della firma è corretto, ma non se i dati firmati sono realmente i dati che dovrebbero essere firmati.

Un malvagio potrebbe modificare il messaggio in questo modo:

<?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>

Questo messaggio è stato verificato correttamente, perché l'elemento firmato e la firma sono sempre gli stessi. Tuttavia, la stringa di dati risultante contiene "FAKE DATA !!".

Ci sono alcuni modi per evitare questo attacco - usando la verifica dello schema contro XSD, controllando l'attributo id dell'elemento fidato ecc. Qual è l'approccio raccomandato per eliminare questo rischio? L'articolo MSDN dovrebbe essere migliorato? Esiste un'implementazione di riferimento che gestisca correttamente questo problema?

    
posta vojta 10.01.2016 - 14:32
fonte

4 risposte

1

Come già detto, questo è il modo in cui è progettato. Probabilmente l'articolo di MSDN potrebbe (o dovrebbe) essere più esplicito a riguardo. Il modo in cui hai estratto i dati "firmati" è sbagliato. La documentazione XMLDSig è chiara a riguardo. Puoi leggere la sezione 8.1 di questo link link

Just as a user should only sign what he or she "sees," persons and automated mechanism that trust the validity of a transformed document on the basis of a valid signature should operate over the data that was transformed (including canonicalization) and signed, not the original pre-transformed data.

e

Note that the use of Canonical XML [XML-C14N] ensures that all internal entities and XML namespaces are expanded within the content being signed. All entities are replaced with their definitions and the canonical form explicitly represents the namespace that an element would otherwise inherit. Applications that do not canonicalize XML content (especially the SignedInfo element) SHOULD NOT use internal entities and SHOULD represent the namespace explicitly within the content being signed since they can not rely upon canonicalization to do this for them. Also, users concerned with the integrity of the element type definitions associated with the XML instance being signed may wish to sign those definitions as well (i.e., the schema, DTD, or natural language description associated with the namespace/identifier).

Second, an envelope containing signed information is not secured by the signature. For instance, when an encrypted envelope contains a signature, the signature does not protect the authenticity or integrity of unsigned envelope headers nor its ciphertext form, it only secures the plaintext actually signed.

Ulteriori dettagli su come convalidare correttamente una firma sono disponibili qui: link

The input to the first Transform is the result of dereferencing the URI attribute of the Reference element.

Ci si dovrebbe fidare solo del risultato del dereferenziamento Riferimenti da SignerInfo. Tutti gli altri dati non devono essere considerati firmati e non devono essere presentati all'utente.

PS: Sì, XMLDSig è molto complesso. E le implementazioni possono facilmente sbagliare.

    
risposta data 20.01.2016 - 19:27
fonte
1

Per me, questo non sembra proprio un errore del protocollo ma un uso errato.

Ottieni una parte dell'XML usando Doc.SelectSingleNode("/message/msgenvelope") ma non hai mai controllato che "/message/msgenvelope" sia effettivamente la parte firmata dell'XML!

Devi trattare l'XML più come un file system con file diversi e non come una entità. Pensa in questo modo: hai tre file: a.exe , b.exe e secure.signature controlli secure.signature e ti dice: il file a.exe è stato firmato da Guy sicuro e non viene modificato. Se esegui b.exe e pensi che sia sicuro, non è un problema di sicurezza della firma.

Questo è fondamentalmente ciò che fai nell'esempio! Chiedi se la firma è valida e torna indietro che è valida. Ma indica anche <Reference URI="#SIGNED_DATA"> che significa che la firma è valida esattamente per #SIGNED_DATA e nient'altro.

Quindi chiedi di ottenere la parte identificata da / message / msgenvelope e trattarla come se avesse una firma valida!

Ma / message / msgenvelope non è chiaramente uguale a #SIGNED_DATA . Potrebbe identificare lo stesso elemento, potrebbe non farlo. Ma sicuramente non hai mai controllato esplicitamente la firma di / message / msgenvelope !

    
risposta data 20.01.2016 - 15:34
fonte
1

La firma XML è soggetta a troppi attacchi. Ciò è dovuto al modo in cui XML viene definito come formato di file e come viene implementato dai framework (parsing).

link

link

    
risposta data 20.01.2016 - 17:18
fonte
1

Aggiungerò questo qui perché non ho visto la risposta specifica per questo caso. La firma XML e le tecnologie correlate sono state notevolmente migliorate in .NET 4.5. È stato aggiunto il supporto per qualsiasi lunghezza della chiave e per le firme SHA256. Ho usato questo codice con successo prima.

Il problema principale è con il tag di riferimento della tua firma:

Dici esplicitamente alla classe SignedXml di guardare SOLO su quell'elemento. Per firmare l'intero documento, l'attributo Uri dovrebbe essere vuoto. Ecco un esempio di un documento XML firmato 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>

Questo documento è stato firmato utilizzando questo codice:

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;
}

Utilizzando questo metodo, l'intero documento è firmato, non solo un singolo elemento.

Puoi verificare la firma in questo modo. La modifica di qualsiasi byte nel file firmato invaliderà 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.");
    }
}

Si noti inoltre che un documento può avere più firme. Ad esempio, puoi mantenere la firma che hai lì sopra, in particolare per quell'elemento, e quindi aggiungere un'altra firma per l'intero documento. Il codice sopra assume il primo elemento della firma trovato.

    
risposta data 23.01.2017 - 04:58
fonte

Leggi altre domande sui tag