How to fix ASP.net MVC Path Traversal
前言
最近碰上資安弱點掃描,ASP.net MVC網站被掃出Path Traversal目錄瀏覽漏洞
才知道原來以往組字串來存取檔案的方式是有風險的Orz
有漏洞風險的程式碼如下
public ActionResult TestFile(string fileName)
{
//App_Data目錄
string dirPath = Server.MapPath("~/App_Data");
//組路徑字串
string filePath = Path.Combine(dirPath, fileName);
if (System.IO.File.Exists(filePath))//檔案存在的話
{
//原本預期下載Excel檔
return File(filePath, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
else
{
return Content("not exists");
}
}
由前端輸入到後端的參數fileName沒有過濾的話,駭客可以輸入fileName=../Web.config來取得Web.config
※雖然IIS預設啟用要求篩選,用戶無法在URL輸入路徑直接下載.config檔,但如果是透過這種駭客手法,就可以由Server端允許回傳.config檔下載
不只下載,如果程式是執行System.IO.File.Delete(filePath);的話,下場也很可怕
遇到這種有漏洞的程式碼,弱點掃描工具大部份會在
System.IO.File.Exists()或System.IO.File.Delete()這兩個方法標記有弱點
ASP.net MVC該如何修正它呢?
實作
先示範第一種錯誤改法
//App_Data目錄
string dirPath = Server.MapPath("~/App_Data");
DirectoryInfo dir = new DirectoryInfo(dirPath);
/*改用DirectoryInfo的searchPattern找檔案*/
FileInfo file = dir.EnumerateFiles(fileName, SearchOption.TopDirectoryOnly).FirstOrDefault();
if (file != null && file.Exists)
{
//原本預期下載Excel檔
return File(file.FullName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
else
{
return Content("not exists");
}
雖然前端無法輸入「../」了
但卻可以向下搜尋
以下是我認為正確的防範寫法,但最後一種才能通過工具檢測(工具也有誤判的時候呀~)
1.對fileName只取檔名
string dirPath = Server.MapPath("~/App_Data");
/*只取檔名,輸入字串就無法向上向下走訪目錄*/
fileName = System.IO.Path.GetFileName(fileName);
string filePath = System.IO.Path.Combine(dirPath, fileName);
if (System.IO.File.Exists(filePath))
{
//原本預期下載Excel檔
return File(filePath, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
else
{
return Content("not exists");
}
2.組出來的filePath再比對一次目錄路徑
string dirPath = Server.MapPath("~/App_Data");
//組檔案路徑字串
string filePath = System.IO.Path.Combine(dirPath, fileName);
/*判斷是否為App_Data目錄*/
if (Path.GetDirectoryName(filePath) == dirPath && System.IO.File.Exists(filePath))
{
//原本預期下載Excel檔
return File(filePath, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
else
{
return Content("not exists");
}
3.列舉該目錄全部檔案再比對檔名來挑檔(這樣寫可以通過CHECKMARX資安檢測)
public ActionResult TestFile(string fileName)
{
//App_Data目錄
string dirPath = Server.MapPath("~/App_Data");
DirectoryInfo dir = new DirectoryInfo(dirPath);
//列舉全部檔案再比對檔名
FileInfo file = dir.EnumerateFiles()
.FirstOrDefault(m=>m.Name==fileName);
if (file!=null && file.Exists)//檔案存在的話
{
//原本預期下載Excel檔
return File(file.FullName, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file.Name);
}
else
{
return Content("not exists");
}
}
以上是我try error無數次情況下,試出檢測工具的接受度XD
Is Path Traversal Vulnerabilities possible in my below code? StackOverFlow討論上
也有人指出用Path.GetInvalidFileNameChars()方法來判斷輸入參數有沒有包含斜線
這也是一種辦法(但能不能通過工具檢測有待試試)
不過如果前端一定要傳給後端包含斜線的字串(例如:/Upload/image.jpg),該網友的辦法就無法寫出正常功能
這種案例在Server端得一定加上Path.GetFileName(fileName),只取檔名,但因為只這樣不會通過工具檢測
所以還得搭配上述第3種寫法,完整程式碼如下:
//參數imgSrc由前端輸入包含斜線的字串
public ActionResult TestFile(string imgSrc)
{
//App_Data目錄
string dirPath = Server.MapPath("~/App_Data");
DirectoryInfo dir = new DirectoryInfo(dirPath);
//只取檔名
string fileName = Path.GetFileName(imgSrc);
//列舉全部檔案再比對檔名
FileInfo file = dir.EnumerateFiles()
.FirstOrDefault(m=>m.Name==fileName);
if (file!=null && file.Exists)//檔案存在的話
{
return Content("exists");
}
else
{
return Content("not exists");
}
}
最後附上資安弱掃工具CHECKMARX的說明