C# xml 常规 保护 方法总结
一 使用xsd模式文件验证xml文件:
xml文件:
<?xml version= "1.0" encoding= "utf-8" ?> <Books> <Book> <Title>ExampleTitle</Title> <Author>John Smith</Author> <Pages>500</Pages> </Book> <Book> <Title>Another Title</Title> <Author>John Doe</Author> <Pages>250</Pages> </Book> </Books> |
xsd文件:
<?xml version= "1.0" encoding= "utf-8" ?> <xs:schema attributeFormDefault= "unqualified" elementFormDefault= "qualified" xmlns:xs= "http://www.w3.org/2001/XMLSchema" > <xs:element name= "Books" > <xs:complexType> <xs:sequence> <xs:element maxOccurs= "unbounded" name= "Book" > <xs:complexType> <xs:sequence> <xs:element name= "Title" type= "xs:string" minOccurs= "1" maxOccurs= "1" /> <xs:element name= "Author" type= "xs:string" /> <xs:element name= "Pages" type= "xs:unsignedShort" minOccurs= "1" maxOccurs= "1" /> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> |
验证代码:
static bool XmlValidate( string xmlPath, string xsdPath, out string message) { bool isXmlValid = true ; string errorMessage = string .Empty; XmlReaderSettings settings = new XmlReaderSettings() { ValidationType = ValidationType.Schema, ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings }; settings.ValidationEventHandler += (o, e) => { isXmlValid = false ; errorMessage = e.Message; }; XmlSchema schema = XmlSchema.Read( new StreamReader(xsdPath), null ); settings.Schemas.Add(schema); XmlDocument xmlDocument = new XmlDocument(); XmlReader xmlReader = XmlReader.Create(xmlPath, settings); xmlDocument.Load(xmlReader); message = errorMessage; return isXmlValid; } |
调用代码:
string xmlPath = "books.xml";
string xsd = "books.xsd";
string message = string.Empty;
bool validate = XmlValidate(xmlPath, xsd, out message);
二xml查询时需要注意XQuery注入(XPath注入),通常xPath语句如下:
static string GetBookTitle( string author, int page) { XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load( "books.xml" ); XPathNavigator navigator = xmlDocument.CreateNavigator(); string xquery = string .Format( "string(//Book[Author/text()='{0}' and Pages/text()={1}]/Title/text())" , author, page.ToString()); XPathExpression expression = navigator.Compile(xquery); return navigator.Evaluate(expression).ToString(); } |
但是这样的代码很容易遭到攻击,这里我们用Mvp.xml(开源库http://mvpxml.codeplex.com/)来实现参数化查询,防止xquery注入。
修改后的代码:
static string GetBookTitle( string author, int page) { XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load( "books.xml" ); string xquery = "string(//Book[Author/text()=$author and Pages/text()=$page]/Title/text())" ; XPathNavigator navigator = xmlDocument.CreateNavigator(); XPathExpression expression = DynamicContext.Compile(xquery); DynamicContext ctx = new DynamicContext(); ctx.AddVariable( "author" , author); ctx.AddVariable( "page" ,page.ToString()); expression.SetContext(ctx); return navigator.Evaluate(expression).ToString(); }<br><br><?xml version= "1.0" encoding= "utf-8" ?> <envelope> <to>ma.jiang@wipro.com</to> < from >gavin_ma@vfc.com</ from > <message>You have just been paid.</message> </envelope> static void Encrypt(XmlDocument document, string elementNameToEntrypt, SymmetricAlgorithm algorithm) { if (document == null ) throw new ArgumentNullException( "document" ); if (elementNameToEntrypt == null ) throw new ArgumentNullException( "elementNameToEntrypt" ); if (algorithm == null ) throw new ArgumentNullException( "key" ); XmlElement elementToEncrypt = document.GetElementsByTagName(elementNameToEntrypt)[0] as XmlElement; if (elementToEncrypt == null ) throw new XmlException( "The specified element was not found" ); EncryptedXml exml = new EncryptedXml(); byte [] encryptedElement = exml.EncryptData(elementToEncrypt, algorithm, false ); EncryptedData encryptedData = new EncryptedData { Type = EncryptedXml.XmlEncElementUrl }; string encryptionMethod = string .Empty; if (algorithm is TripleDES) encryptionMethod = EncryptedXml.XmlEncTripleDESUrl; else if (algorithm is DES) encryptionMethod = EncryptedXml.XmlEncDESUrl; else if (algorithm is Rijndael) { switch (algorithm.KeySize) { case 128: encryptionMethod = EncryptedXml.XmlEncAES128Url; break ; case 192: encryptionMethod = EncryptedXml.XmlEncAES192Url; break ; case 256: encryptionMethod = EncryptedXml.XmlEncAES256Url; break ; } } else { throw new CryptographicException( "Specificed algorithm is not supported" ); } encryptedData.EncryptionMethod = new EncryptionMethod(encryptionMethod); encryptedData.CipherData.CipherValue = encryptedElement; EncryptedXml.ReplaceElement(elementToEncrypt, encryptedData, false ); } static void Decrypt(XmlDocument document, SymmetricAlgorithm algorithm) { if (document == null ) throw new ArgumentNullException( "document" ); if (algorithm == null ) throw new ArgumentNullException( "key" ); XmlElement encryptedElement = document.GetElementsByTagName( "EncryptedData" )[0] as XmlElement; if (encryptedElement == null ) throw new XmlException( "No encrypted element was found." ); EncryptedData encryptedData = new EncryptedData(); encryptedData.LoadXml(encryptedElement); EncryptedXml encryptedXml = new EncryptedXml(); byte [] rgbOutput = encryptedXml.DecryptData(encryptedData, algorithm); encryptedXml.ReplaceData(encryptedElement,rgbOutput); } |
采用对称加密,加密xml节点新建xml文件如下:
加密和解密代码如下:
调用方法如下:
string xmlPath = "encrypt.xml";
XmlDocument document = new XmlDocument();
document.Load(xmlPath);
RijndaelManaged rijndael = new RijndaelManaged();
Encrypt(document, "message", rijndael);
Decrypt(document, rijndael);
加密后的数据如图:
采用非对成加密技术,加密xml节点
加密和解密代码如下:
public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg, string KeyName) { // Check the arguments. if (Doc == null ) throw new ArgumentNullException( "Doc" ); if (ElementToEncrypt == null ) throw new ArgumentNullException( "ElementToEncrypt" ); if (EncryptionElementID == null ) throw new ArgumentNullException( "EncryptionElementID" ); if (Alg == null ) throw new ArgumentNullException( "Alg" ); if (KeyName == null ) throw new ArgumentNullException( "KeyName" ); //////////////////////////////////////////////// // Find the specified element in the XmlDocument // object and create a new XmlElemnt object. //////////////////////////////////////////////// XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement; // Throw an XmlException if the element was not found. if (elementToEncrypt == null ) { throw new XmlException( "The specified element was not found" ); } RijndaelManaged sessionKey = null ; try { ////////////////////////////////////////////////// // Create a new instance of the EncryptedXml class // and use it to encrypt the XmlElement with the // a new random symmetric key. ////////////////////////////////////////////////// // Create a 256 bit Rijndael key. sessionKey = new RijndaelManaged(); sessionKey.KeySize = 256; EncryptedXml eXml = new EncryptedXml(); byte [] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false ); //////////////////////////////////////////////// // Construct an EncryptedData object and populate // it with the desired encryption information. //////////////////////////////////////////////// EncryptedData edElement = new EncryptedData(); edElement.Type = EncryptedXml.XmlEncElementUrl; edElement.Id = EncryptionElementID; // Create an EncryptionMethod element so that the // receiver knows which algorithm to use for decryption. edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url); // Encrypt the session key and add it to an EncryptedKey element. EncryptedKey ek = new EncryptedKey(); byte [] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false ); ek.CipherData = new CipherData(encryptedKey); ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url); // Create a new DataReference element // for the KeyInfo element. This optional // element specifies which EncryptedData // uses this key. An XML document can have // multiple EncryptedData elements that use // different keys. DataReference dRef = new DataReference(); // Specify the EncryptedData URI. dRef.Uri = "#" + EncryptionElementID; // Add the DataReference to the EncryptedKey. ek.AddReference(dRef); // Add the encrypted key to the // EncryptedData object. edElement.KeyInfo.AddClause( new KeyInfoEncryptedKey(ek)); // Set the KeyInfo element to specify the // name of the RSA key. // Create a new KeyInfoName element. KeyInfoName kin = new KeyInfoName(); // Specify a name for the key. kin.Value = KeyName; // Add the KeyInfoName element to the // EncryptedKey object. ek.KeyInfo.AddClause(kin); // Add the encrypted element data to the // EncryptedData object. edElement.CipherData.CipherValue = encryptedElement; //////////////////////////////////////////////////// // Replace the element from the original XmlDocument // object with the EncryptedData element. //////////////////////////////////////////////////// EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false ); } catch (Exception e) { // re-throw the exception. throw e; } finally { if (sessionKey != null ) { sessionKey.Clear(); } } } public static void Decrypt(XmlDocument Doc, RSA Alg, string KeyName) { // Check the arguments. if (Doc == null ) throw new ArgumentNullException( "Doc" ); if (Alg == null ) throw new ArgumentNullException( "Alg" ); if (KeyName == null ) throw new ArgumentNullException( "KeyName" ); // Create a new EncryptedXml object. EncryptedXml exml = new EncryptedXml(Doc); // Add a key-name mapping. // This method can only decrypt documents // that present the specified key name. exml.AddKeyNameMapping(KeyName, Alg); // Decrypt the element. exml.DecryptDocument(); } |
代码调用如下:
string xmlPath = "encrypt.xml";
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = true;
document.Load(xmlPath);
UnicodeEncoding ByteConverter = new UnicodeEncoding();
CspParameters cspParams = new CspParameters { KeyContainerName = "XML_ENC_RSA_KEY" };
using ( RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams))
{
Encrypt(document, "message", "EncryptedElementID", rsaKey, "rsaKey");
// document.Save("test.xml");
Decrypt(document, rsaKey, "rsaKey");
};
运行结果如图:
用非对称密钥签名xml
实现代码如下:

static void SignXml(XmlDocument document, RSA algorithm) { SignedXml signxml = new SignedXml(document) { SigningKey = algorithm }; Reference reference = new Reference { Uri = string.Empty }; XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); reference.AddTransform(env); signxml.AddReference(reference); signxml.ComputeSignature(); XmlElement xmlDigitalSignature = signxml.GetXml(); document.DocumentElement.AppendChild(document.ImportNode(xmlDigitalSignature,true)); } static bool IsSignedXmlValid(XmlDocument document, RSA algorithm) { SignedXml signxml = new SignedXml(document); XmlNodeList nodelist = document.GetElementsByTagName("Signature"); if (nodelist.Count < 1) { throw new CryptographicException("No signature found"); } signxml.LoadXml((XmlElement)nodelist[0]); return signxml.CheckSignature(algorithm); }
调用代码如下:
string xmlPath = "encrypt.xml";
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = true;
document.Load(xmlPath);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
SignXml(document, rsa);
IsSignedXmlValid(document, rsa);
运行结果如图:
个人认为证书在xml中使用情况不常见,并且也比较简单,这里就省略了。
总结一下:
在使用xml之前,必须使用严格的架构(模式文件)来验证,尽可能使用架构的本地副本,以便缓存解析器来缓存他们。
选择一个合适的加密算法,如果应用程序需要加密和解密相同的应用程序,那么选择对称加密;应用程序需要和外部系统进行交流,那么选择非对称加密。
如果需要确保数据没有被更改,就需要始终使用数字签名
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构