ASP.NET 上传文件安全检测方案

一、前端初步过滤(防误操作)

复制代码
<!-- 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"
相关推荐
余衫马19 小时前
Windows 10 环境下 Redis 编译与运行指南
redis·后端
青柠编程1 天前
基于Spring Boot的竞赛管理系统架构设计
java·spring boot·后端
虚行1 天前
C#上位机 通过ProfitNet连接西门子PLC教程--系统模拟环境搭建
学习·c#·plc
s9123601011 天前
【rust】 pub(crate) 的用法
开发语言·后端·rust
雪域迷影1 天前
C++/C#游戏开发引擎和2D/3D图形库
c++·3d·c#
夕颜1111 天前
关于排查问题的总结
后端
码事漫谈1 天前
揭秘RAG的核心引擎:Document、Embedding与Retriever详解
后端
码事漫谈1 天前
BM25 检索是什么
后端
周杰伦fans1 天前
Visual Studio 插件 - 喝水提醒
c#·visual studio
sali-tec1 天前
C# 基于halcon的视觉工作流-章38-单位转换
开发语言·人工智能·数码相机·算法·计算机视觉·c#