再次和Fortify神鬼交鋒(新的Rule Pack上市),Fortify SCA新檢測出Path Manipulation及XML External Entity Injection(Input Validation and Representation, Data Flow),來筆記這次的修正。
先一下看他們的模樣
Path Manipulation: Critical等級
XML External Entity Injection: High等級
簡單歸納
歸納出一個現象,只要程式行透過xml檔案內的參數來建立或刪除檔案時就會對於注入發出警示!!!
- 讀取xml檔案的程式行被檢測出High等級的XML External Entity Injection(Input Validation and Representation, Data Flow)。
- 使用到xml取出檔案路徑來建立檔案的程式行則被檢測Critical等級的Path Manipulation。
衝擊與準確度象限
因為包含Critical等級的issue,有注入的風險,不能閉上眼睛。
修正方法
增加了許多檔案路徑及檔名的檢查來避免注入(injection),但還是徒勞無功,準備放棄之際,將xmlDocument改用LINQ To XML(XElement),兩個Fortify的issue瞬間消除,決定要轉圈圈灑花來慶祝。
要讀取的xml檔案內容,找FILE_SETTING節點下的URL值
<FILE_SETTING>
<FILE_NAME>MemberList</FILE_NAME>
<URL>D:\AP\Airlines\MemberList.txt</URL>
</FILE_SETTING>
舊版xmldocument寫法(被檢測出問題的)
程式中使用xmlDocument讀取xml檔案,並且使用檔案中的參數(攻擊點)來建立檔案。
void GenerateFileLegacy()
{
System.Xml.XmlDocument xdom = new System.Xml.XmlDocument();
xdom.Load(@"D:\AP\FileSetting.xml");
System.Xml.XmlNodeList ds = xdom.DocumentElement.SelectNodes("FILE_SETTING");
for (int i = 0; i < ds.Count; i++)
{
string URL = ds[i]["URL"].InnerText;
using (FileStream fs = new FileStream(URL, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (StreamWriter srOutFile = new StreamWriter(fs, Encoding.Default))
{
}
}
}
}
使用XElement(一次解決兩個issue)
其實也還是有注入的風險,但fortify似乎by pass了。
void GenerateFile()
{
XElement xelement = XElement.Load(@"D:\AP\FileSetting.xml");
var fileSettings = from filexml in xelement.Elements("FILE_SETTING")
select filexml;
foreach (XElement xEle in fileSettings)
{
string URL = xEle.Element("URL") == null ? "" : xEle.Element("URL").Value;
using (FileStream fs = new FileStream(URL, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (StreamWriter srOutFile = new StreamWriter(fs, Encoding.Default))
{
}
}
}
}
0個Critical,0個High
小結
雖然這樣改寫可以解決issue,但為了避免可能的注入風險,除了建立檔案白名單外,也可以加上作業系統路徑、檢查附檔名等幾種檔案檢核項目。
private static readonly string[] extensions = { ".pdf", ".txt" ,".xls", ".xlsx", "doc", "docx"};
bool IsValidURI(string URL)
{
if (URL.Contains(Environment.GetFolderPath(Environment.SpecialFolder.Windows))) return false;
if (URL.Contains(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86))) return false;
if (!URL.Contains(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)))return false;
if (!extensions.Any(p => p == Path.GetExtension(URL))) return false;
return true;
}
還要神鬼交鋒很久..
參考
Effective Xml Part 1: Choose the right API