Missing XML Validation

我們有程式被掃出 Missing XML Validation 的問題,
發生的地方是在 XmlReader.Create(  ...

要如何解決呢?

報告的說明如下,

XmlReader.Create() 方法並未採納 XmlReaderSettings 物件, 這表示不會執行驗證。

這讓攻擊者有機會提供惡意輸入。

原本的程式類似如下,

public static T ReadObjectFromXmlFile<T>(string filePath)
{
	string safeFilePath = filePath;
	T readObject = default(T);
	if (!File.Exists(safeFilePath))
	{
		throw new FileNotFoundException($"xml file:{filePath} not found!");
	}
	using (var fileStream = new FileStream(safeFilePath, FileMode.Open, FileAccess.Read))
	{
		//報告說,這裡要有 XmlReaderSettings 哦!
		var reader = XmlReader.Create(fileStream);
		var serializer = new XmlSerializer(typeof(T));
		readObject = (T)serializer.Deserialize(reader);
		reader.Close();
		fileStream.Close();
	}
	return readObject;
}

 

所以我們可參考「Validating an XML against referenced XSD in C#」的方式,

多加入 XmlReaderSettings  設定,我們可針對 Validation 的結果 Log 下來,

修改後的 Method 如下,

public static T ReadObjectFromXmlFileValidation<T>(string filePath)
{
	string safeFilePath = filePath;
	T readObject = default(T);
	if (!File.Exists(safeFilePath))
	{
		throw new FileNotFoundException($"xml file:{filePath} not found!");
	}
	using (var fileStream = new FileStream(safeFilePath, FileMode.Open, FileAccess.Read))
	{
		// Set the validation settings.
		XmlReaderSettings settings = new XmlReaderSettings();
		settings.ValidationType = ValidationType.Schema;
		settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
		settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
		settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
		settings.ValidationEventHandler += (sender, args) =>
		{
			//有需要log的話,請handle ValidationEventHandler 
			if (args.Severity == XmlSeverityType.Warning)
				Console.WriteLine($"\tWarning: Matching schema not found.  No validation occurred.{args.Message}");
			else
				Console.WriteLine($"\tValidation error: {args.Message}");
		};
        //XmlReader.Create 要給 XmlReaderSettings
		var reader = XmlReader.Create(fileStream, settings);
		var serializer = new XmlSerializer(typeof(T));
		readObject = (T)serializer.Deserialize(reader);
		reader.Close();
		fileStream.Close();
	}
	return readObject;
}

 

所以整個  Console 測試程式 如下,

class Program
{
	static void Main(string[] args)
	{
		string xmlFile = @"c:\Bookmark.xml";
		var bk1 = ReadObjectFromXmlFile<Bookmark>(xmlFile);
		var bk2 = ReadObjectFromXmlFileValidation<Bookmark>(xmlFile);
	}

	public static T ReadObjectFromXmlFile<T>(string filePath)
	{
		string safeFilePath = filePath;
		T readObject = default(T);
		if (!File.Exists(safeFilePath))
		{
			throw new FileNotFoundException($"xml file:{filePath} not found!");
		}
		using (var fileStream = new FileStream(safeFilePath, FileMode.Open, FileAccess.Read))
		{
			var reader = XmlReader.Create(fileStream);
			var serializer = new XmlSerializer(typeof(T));
			readObject = (T)serializer.Deserialize(reader);
			reader.Close();
			fileStream.Close();
		}
		return readObject;
	}


	//http://stackoverflow.com/questions/751511/validating-an-xml-against-referenced-xsd-in-c-sharp
	public static T ReadObjectFromXmlFileValidation<T>(string filePath)
	{
		string safeFilePath = filePath;
		T readObject = default(T);
		if (!File.Exists(safeFilePath))
		{
			throw new FileNotFoundException($"xml file:{filePath} not found!");
		}
		using (var fileStream = new FileStream(safeFilePath, FileMode.Open, FileAccess.Read))
		{
			// Set the validation settings.
			XmlReaderSettings settings = new XmlReaderSettings();
			settings.ValidationType = ValidationType.Schema;
			settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
			settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
			settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
			settings.ValidationEventHandler += (sender, args) =>
			{
				//有需要log的話,請handle ValidationEventHandler 
				if (args.Severity == XmlSeverityType.Warning)
					Console.WriteLine($"\tWarning: Matching schema not found.  No validation occurred.{args.Message}");
				else
					Console.WriteLine($"\tValidation error: {args.Message}");
			};
			//XmlReader.Create 要給 XmlReaderSettings
			var reader = XmlReader.Create(fileStream, settings);
			var serializer = new XmlSerializer(typeof(T));
			readObject = (T)serializer.Deserialize(reader);
			reader.Close();
			fileStream.Close();
		}
		return readObject;

	}
}

public class Bookmark
{
	/// <summary>
	/// 收藏種類
	/// </summary>
	public enum BookmarkType
	{
		Forum,     //討論版
		Topic      //主題
	}

	/// <summary>
	/// 收藏ID
	/// </summary>
	public int BookmarkId { get; set; }
	/// <summary>
	/// 版本
	/// </summary>
	public int Flag { get; set; }
	/// <summary>
	/// 收藏對象ID
	/// </summary>
	public int TargetId { get; set; }
	/// <summary>
	/// 收藏對象種類
	/// </summary>
	public BookmarkType TargetType { get; set; }
	/// <summary>
	/// 建立者
	/// </summary>
	public string CreatorId { get; set; }
	/// <summary>
	/// 建立時間
	/// </summary>
	public DateTime CeationDatatime { get; set; }

}

Bookmark.xml 內容如下,

<?xml version="1.0" encoding="utf-8"?>
<Bookmark xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <BookmarkId>1</BookmarkId>
  <Flag>2</Flag>
  <TargetId>3</TargetId>
  <TargetType>Topic</TargetType>
  <CeationDatatime>2017-01-01T15:12:09</CeationDatatime>
</Bookmark>

執行結果如下,

註:補充同事 Cigala 分享,不使用 XmlReader.Create Method 也是一個方式,如下,

public static T ReadObjectFromXmlFile<T>(string filePath)
{
string safeFilePath = GetSafePath(filePath);
T readObject = default(T);
if (File.Exists(safeFilePath))
{
StreamReader fileStream = null;
try
{
fileStream = new StreamReader(safeFilePath);

var serializer = new XmlSerializer(typeof(T));
readObject = (T)serializer.Deserialize(fileStream);
}
finally
{
if (fileStream != null)
{
fileStream.Close();
}
fileStream = null;
}
}
return readObject;
}

 

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^