一、前端初步过滤(防误操作)
<!-- HTML部分 -->
<input type="file" id="fileUpload" accept=".jpg,.png,.pdf,.docx" />
<button onclick="validateFile()">上传</button>
<script>
function
validateFile()
{
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 FF
return buffer[0] == 0xFF && buffer[1] == 0xD8 && buffer[2] == 0xFF;
case ".png":
// PNG魔数: 89 50 4E 47 0D 0A 1A 0A
return 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 46
return 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"