ASP.NET MVC 5 實作更安全的檔案上傳功能 (ASP.NET MVC Safer File Upload Implements)
2021-02-26
不當的檔案上傳功能,可能導致被植入後門程式(webshell)或者惡意的下載檔擴散影響至網站使用者,筆記 ASP.NET MVC 如何實作更安全的檔案上傳機制,同時也探討網頁伺服器可以在那些層面協助,讓網站應用程式更為安全 🐱💻
說明
常見的資安弱點
- 大量的檔案癱瘓應用程式伺服器儲存空間
- 惡意使用者上傳後門 webshell (asp, php, etc)
網頁伺服器的聯合防禦
- 確認 UploadFiles 資料夾 Handler 沒有提供 執行 權限
- 要求篩選(Request Filtering) 設定副檔名白名單
- 限定允許的 MIME(ContentType) 類型
上傳程式碼
Controller
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
var fileValid = true;
// Limit File Szie Below : 5MB
if (file.ContentLength <= 0 || file.ContentLength > 5242880)
{
fileValid = false;
}
if (file.ContentType != "image/png" || file.ContentType != "image/jpeg")
{
fileValid = false;
}
if (fileValid == true)
{
string extension = Path.GetExtension(file.FileName);
string fileName = $"{Guid.NewGuid()}{extension}";
string savePath = Path.Combine(Server.MapPath("~/UploadFile"), fileName);
file.SaveAs(savePath);
ViewBag.Message = "檔案上傳成功。";
}
else
{
ViewBag.Message = "Upload Failed 😫";
}
return View();
}
其中存入伺服器資料夾的檔案名稱,使用 Guid.NewGuid()
來產生混淆,避免讓惡意上傳者直接猜到圖片生成的路徑。
此外如果限制上傳的檔案必須為圖片,可以在利用 System.Drawing.Image.FromStream
來檢查上傳的檔案是否符合:
[NonAction]
public bool CheckIsImage(Stream imageStream)
{
bool check;
try
{
System.Drawing.Image.FromStream(imageStream);
check = true;
}
catch
{
check = false;
}
return check;
}
CheckIsImage 檢查的方式
if (!CheckIsImage(file.InputStream))
{
fileValid = false;
}
View
@using (Html.BeginForm(
"Upload", "Upload", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
@Html.TextBox("file", "", new { type = "file" }) <br />
<input type="submit" value="Upload" /> <br />
@ViewBag.Message
</div>
}
注意: enctype 要使用 multipart/form-data
注意: File input 如果不是使用 Html.TextBox
要注意要主動給予 Name Attributes,且必須與 Controller 中 Action HttpPostedFileBase 所接收的參數名稱一致,否則會發現上傳但卻是 Null 的問題。
關於 enctype 的筆記,請參考 HTML Form Enctype
資安實踐
實作安全的下載機制
[NonAction]
public string FileNameMapping(string fileName)
{
return "mapping.png";
}
public ActionResult Download(string fileName)
{
var convertFileName = FileNameMapping(fileName);
return File(
Path.Combine(
Server.MapPath("~/UploadFiles"), convertFileName)
, "image/png"
);
}
Download Action 必須要限制授權存取,並檢查存取者是否有要存取檔案的權限。
其中 FileNameMapping 需要利用資料庫對照表、Base64編碼,來轉換混淆後的檔案名稱回原本的檔案名稱,此外也可以藉由 FileNameMapping 思考是否會存在 Path Traversal 的問題。
避免檔案上傳資料夾被直接取用
web.config 可以設定隱藏區段的方式處理
<system.webServer>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="UploadFiles" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
相關連結
ASP.NET MVC 5 實作更安全的檔案下載功能 (ASP.NET MVC Safer Downloads Implements)