XML署名検証に基づく多くのアプリケーションに問題がある可能性があります(もちろん、私が間違っていなければ)。
エンベロープされたXML署名を持つ単純なXMLメッセージを見てみましょう。
<?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>
この [〜#〜] msdn [〜#〜] 記事によると、このようなXMLドキュメントを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
}
私は問題があると思います:CheckSignature
メソッドは署名の値が正しいかどうかを検証しますが、署名されたデータが実際に署名されることが期待されるデータであるかどうかは検証しません。
悪者はメッセージを次のように変更できます。
<?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>
署名された要素と署名がまだ同じであるため、このメッセージは正しく検証されます。ただし、結果のデータ文字列には「FAKE DATA !!」が含まれます。
この攻撃を回避する方法はいくつかあります。XSDに対するスキーマ検証を使用する、信頼できる要素のid
属性をチェックするなどです。このリスクを取り除くには、どのようなアプローチが推奨されますか? MSDN記事を改善すべきですか?この問題を正しく処理するリファレンス実装はありますか?
すでに述べたように、これは仕様によるものです。おそらく、MSDNの記事は、それについてより明示的である可能性があります(またはすべきです)。 「署名された」データを抽出した方法が間違っています。 XMLDSigのドキュメントはそれについて明確です。このリンクのセクション8.1をお読みください https://www.w3.org/TR/xmldsig-core/
ユーザーが「見たもの」にのみ署名する必要があるのと同じように、有効な署名に基づいて変換されたドキュメントの有効性を信頼する人と自動化されたメカニズムは、変換されたデータ(正規化を含む)に対して動作し、署名されません。元の変換済みデータ。
そして
Canonical XML [XML-C14N]を使用すると、署名されるコンテンツ内ですべての内部エンティティとXML名前空間が確実に展開されることに注意してください。すべてのエンティティはそれらの定義に置き換えられ、正規形式は要素が継承する名前空間を明示的に表します。 XMLコンテンツ(特にSignedInfo要素)を正規化しないアプリケーションは、内部エンティティを使用してはならず(SHOULD NOT)、署名対象のコンテンツ内で名前空間を明示的に表す必要があります。これは、正規化に頼ることができないためです。また、署名されるXMLインスタンスに関連付けられている要素タイプ定義の整合性に関心があるユーザーは、それらの定義(つまり、名前空間/識別子に関連付けられているスキーマ、DTD、または自然言語記述)にも署名することを望む場合があります。
第二に、署名された情報を含む封筒は署名によって保護されていません。たとえば、暗号化されたエンベロープに署名が含まれている場合、署名は署名されていないエンベロープヘッダーの真正性や整合性、およびその暗号文形式を保護せず、実際に署名された平文のみを保護します。
署名を正しく検証する方法の詳細については、こちらをご覧ください https://www.w3.org/TR/xmldsig-core/#sec-CoreValidation
最初のTransformへの入力は、Reference要素のURI属性を逆参照した結果です。
参照をSignerInfoから逆参照した結果のみを信頼する必要があります。他のすべてのデータは署名されたと見なされるべきではなく、ユーザーに提示されるべきではありません。
PS:はい、XMLDSigは非常に複雑です。そして、実装は簡単にそれを誤解することができます。
私には、これは実際にはプロトコルのエラーではなく、誤って使用されているように見えます。
Doc.SelectSingleNode("/message/msgenvelope")
を使用してXMLの一部を取得しますが、これを確認したことはありません"/message/msgenvelope"
は実際にはXMLの署名された部分です!
XMLは、1つのエンティティではなく、さまざまなファイルを持つファイルシステムのように扱う必要があります。次のように考えてください。3つのファイルがあります:a.exe、b.exeとsecure.signaturesecure.signatureを確認すると、あなた:ファイルa.exeはTrusted Guyによって署名されており、変更されていません。次にb.exeを実行してこれが安全だと思う場合、それは署名のセキュリティ上の問題ではありません。
これは基本的に例で行うことです!署名が有効かどうかを確認し、有効であることを確認します。ただし、<Reference URI="#SIGNED_DATA">
これは、署名が#SIGNED_DATAに対してのみ有効であり、それ以外は有効でないことを意味します。
次に、/ message/msgenvelopeで識別される部分を取得し、有効な署名があるように処理するように依頼します。
ただし、/ message/msgenvelopeは#SIGNED_DATAとは明らかに異なります。同じ要素を特定する場合とそうでない場合があります。ただし、/ message/msgenvelopeの署名を明示的に確認したことは決してないでしょう。
このケースの具体的な答えを見たことがないので、ここに追加します。 .NET 4.5では、XML署名と関連テクノロジが大幅に改善されました。任意のキー長、およびSHA256署名のサポートが追加されました。私は以前このコードをうまく使用しました。
主な問題は、署名の参照タグにあります。
SignedXmlクラスにその要素のみを参照するように明示的に指示します。ドキュメント全体に署名するには、Uri属性を空にする必要があります。次に、SHA-256署名済みXMLドキュメントの例を示します。
<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>
このドキュメントは、次のコードを使用して署名されました:
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;
}
この方法を使用すると、単一の要素だけでなく、ドキュメント全体が署名されます。
このようにして署名を検証できます。署名済みファイルのバイトを変更すると、署名が無効になります。
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.");
}
}
また、ドキュメントには複数の署名が含まれる場合があることに注意してください。たとえば、特にその要素について、そこにある署名を保持し、ドキュメント全体に別の署名を追加できます。上記のコードは、最初に見つかった署名要素を想定しています。
XML署名は非常に多くの攻撃を受ける傾向があります。これは、ファイル形式としてのXMLの定義方法と、フレームワークによる(解析)フレームワークによる実装方法が原因です。
https://www.owasp.org/images/5/5a/07A_Breaking_XML_Signature_and_Encryption_-_Juraj_Somorovsky.pdf
https://lists.w3.org/Archives/Public/public-xmlsec/2009Nov/att-0019/Camera-Ready.pdf