我們有程式被掃出 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:從零開始的軟體開發生活」
請大家繼續支持 ^_^