一、前端初步過濾(防誤操作)
<!-- HTML部分 --><input type="file" id="fileUpload" accept=".jpg,.png,.pdf,.docx" /><button onclick="validateFile()">上傳</button><script>functionvalidateFile(){const file = document.getElementById('fileUpload').files[0];if (!file) return alert('請選擇文件');// 檢查文件擴展名const allowedExtensions = /(\.jpg|\.png|\.pdf|\.docx)$/i;if (!allowedExtensions.test(file.name)){ alert('不允許的文件類型'); return false; }// 檢查文件大小(示例:限制20MB)if (file.size > 20 * 1024 * 1024){ alert('文件超過20MB'); return false; }// 提交表單document.getElementById('uploadForm').submit();}</script>
二、后端深度驗證(核心防御)
// 1. 驗證文件擴展名(雙重驗證,防止繞過前端)
// 2. 驗證MIME類型(檢查Content-Type頭)
// 3. 驗證文件內容(魔數檢測)
// 4. 掃描文件是否包含惡意代碼(示例:檢查HTML文件中的腳本標簽)
// 5. 保存文件到安全位置(禁用執行權限)
// ASP.NET MVC控制器示例[HttpPost][ValidateAntiForgeryToken]public ActionResult Upload(HttpPostedFileBase file){if (file == null || file.ContentLength == 0)return Json(new { success = false, message = "請選擇文件" });try{// 1. 驗證文件擴展名(雙重驗證,防止繞過前端)var allowedExtensions = new[] { ".jpg", ".png", ".pdf", ".docx" };var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();if (!allowedExtensions.Contains(fileExtension))return Json(new { success = false, message = "文件類型不允許" });// 2. 驗證MIME類型(檢查Content-Type頭)if (!IsValidMimeType(file.ContentType, fileExtension))return Json(new { success = false, message = "文件類型不匹配" });// 3. 驗證文件內容(魔數檢測)if (!IsValidFileContent(file.InputStream, fileExtension))return Json(new { success = false, message = "文件內容異常" });// 4. 掃描文件是否包含惡意代碼(示例:檢查HTML文件中的腳本標簽)if (fileExtension == ".html" && ContainsMaliciousCode(file.InputStream))return Json(new { success = false, message = "文件包含惡意代碼" });// 5. 保存文件到安全位置(禁用執行權限)var fileName = Guid.NewGuid().ToString() + fileExtension;var filePath = Path.Combine(Server.MapPath("~/Uploads/"), fileName);file.SaveAs(filePath);return Json(new { success = true, message = "上傳成功" });}catch (Exception ex){// 記錄詳細日志(包含文件名、大小、時間戳等)Logger.Error($"文件上傳失敗: {ex.Message}", ex);return Json(new { success = false, message = "上傳過程中發生錯誤" });}}// 驗證MIME類型private bool IsValidMimeType(string contentType, string fileExtension){var allowedMimeTypes = new Dictionary<string, string[]>{{ ".jpg", new[] { "image/jpeg", "image/pjpeg" } },{ ".png", new[] { "image/png" } },{ ".pdf", new[] { "application/pdf" } },{ ".docx", new[] { "application/vnd.openxmlformats-officedocument.wordprocessingml.document" } }};if (!allowedMimeTypes.ContainsKey(fileExtension))return false;return allowedMimeTypes[fileExtension].Contains(contentType);}// 驗證文件內容(魔數檢測)private bool IsValidFileContent(Stream stream, string fileExtension){// 重置流位置stream.Position = 0;// 讀取文件前幾個字節(魔數)var buffer = new byte[8];stream.Read(buffer, 0, buffer.Length);stream.Position = 0; // 重置流位置供后續使用// 根據文件類型檢查魔數switch (fileExtension){case ".jpg":// JPEG魔數: FF D8 FFreturn buffer[0] == 0xFF && buffer[1] == 0xD8 && buffer[2] == 0xFF;case ".png":// PNG魔數: 89 50 4E 47 0D 0A 1A 0Areturn buffer[0] == 0x89 && buffer[1] == 0x50 && buffer[2] == 0x4E && buffer[3] == 0x47 && buffer[4] == 0x0D && buffer[5] == 0x0A && ??????????????????buffer[6] == 0x1A && buffer[7] == 0x0A; ???case ".pdf":// PDF魔數: 25 50 44 46return buffer[0] == 0x25 && buffer[1] == 0x50 && buffer[2] == 0x44 && buffer[3] == 0x46;default:// 其他類型可以添加更多檢查return true;}}// 檢查文件是否包含惡意代碼(示例:HTML文件)private bool ContainsMaliciousCode(Stream stream){using (var reader = new StreamReader(stream)){var content = reader.ReadToEnd();stream.Position = 0; // 重置流位置// 檢測常見的惡意代碼模式//在這里增加被攻擊的惡意代碼var maliciousPatterns = new[] {@"<script\s*[^>]*>",@"vbscript:",@"onerror\s*=",@"<iframe\s*[^>]*>"};return maliciousPatterns.Any(pattern =>Regex.IsMatch(content, pattern, RegexOptions.IgnoreCase));}}@"<?xml", // XML注入@"<!DOCTYPE", // XXE攻擊@"<script", // 腳本注入@"<img", // 圖片標簽注入@"<iframe", // iframe注入@"<object", // object標簽@"<embed", // embed標簽@"<applet", // applet標簽@"<form", // form標簽@"<input", // input標簽@"<link", // link標簽@"<style", // style標簽@"<svg", // SVG注入@"<math", // MathML注入@"onerror", // 事件處理器@"onload", // 事件處理器@"javascript:", // JS協議@"data:text/html", // data協議@"base64,", // base64編碼@"eval(", // eval函數@"expression(", // CSS表達式@"alert(", // alert函數@"document.cookie", // 訪問cookie@"window.location", // 重定向@"window.open", // 彈窗@"fetch(", // fetch API@"XMLHttpRequest", // AJAX@"ActiveXObject", // ActiveX@"<meta", // meta標簽@"<body", // body標簽@"<head", // head標簽@"<title", // title標簽@"<audio", // audio標簽@"<video", // video標簽@"<source", // source標簽@"<track", // track標簽@"<marquee", // marquee標簽@"<blink", // blink標簽@"<bgsound", // bgsound標簽@"<frame", // frame標簽@"<frameset", // frameset標簽@"<noscript", // noscript標簽@"<plaintext", // plaintext標簽@"<xss", // xss標簽@"vbscript:", // vbscript協議@"mocha:", // mocha協議@"livescript:", // livescript協議@"<!--", // 注釋@"-->", // 注釋@"<%=", // ASP/模板注入@"<?php", // PHP代碼// @"<%!", // JSP/模板注入@"<%#", // ASP.NET模板注入@"<%$", // ASP.NET模板注入@"<%:", // ASP.NET模板注入@"<%?", // 模板注入@"<%--", // 注釋@"<%>", // 模板@"<c:", // JSTL標簽@"<jsp:", // JSP標簽@"<s:", // Struts標簽// @"<%@", // 指令// @"<#", // Freemarker@"#include", // SSI@"system(", // 系統命令@"exec(", // 系統命令@"passthru(", // 系統命令@"shell_exec(", // 系統命令@"popen(", // 系統命令@"proc_open(", // 系統命令@"require", // PHP@"include", // PHP@"require_once", // PHP@"include_once", // PHP@"import", // Python/JS// @"from", // Python@"os.system", // Python@"subprocess", // Python@"rm -rf", // Linux命令@"del ", // Windows命令@"request",@"eval",@"cmd"