You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中验证上传文件真实类型、防范扩展名篡改恶意文件的可靠方案

上传文件真实格式校验方案(C#实现)

要实现篡改扩展名后仍有效的文件格式校验,核心逻辑是绕过扩展名直接校验文件本身的二进制特征,配合多层防御规则避免恶意文件绕过。

核心校验逻辑

  • 第一层:基础扩展名校验,过滤明显不符合要求的请求,降低后续校验压力
  • 第二层:魔术数字(文件头)校验,不同格式的文件开头固定字节是唯一标识,修改扩展名不会改变该特征
  • 第三层:文件内容特征校验,避免攻击者单独伪造文件头绕过校验
  • 第四层:上传后存储规则加固,即使有漏网的恶意文件也无法被执行

允许格式的魔术数字参考

  • PDF:文件前5个字节固定为 25 50 44 46 2D,对应ASCII编码为%PDF-
  • DOC(Word 97-2003版):属于OLE复合文档,前8个字节固定为 D0 CF 11 E0 A1 B1 1A E1,注意XLS、PPT也是同OLE头,需要额外校验Word专属特征
  • TXT:无统一固定魔术数字,需通过排除二进制特征、可执行代码特征完成校验

C# 实现代码示例

using System;
using System.Linq;
using System.IO;
using System.Text;

public static class FileFormatValidator
{
    // 校验入口,传入上传文件流和原始文件名
    public static bool ValidateUploadedFile(Stream fileStream, string fileName)
    {
        // 1. 基础扩展名校验
        var allowedExtensions = new[] { ".doc", ".txt", ".pdf" };
        var fileExt = Path.GetExtension(fileName).ToLowerInvariant();
        if (!allowedExtensions.Contains(fileExt)) return false;

        // 读取前20字节做文件头校验
        var headerBuffer = new byte[20];
        int readLength = fileStream.Read(headerBuffer, 0, headerBuffer.Length);
        if (readLength < 5) return false; // 过短文件直接判定非法
        fileStream.Position = 0; // 复位流位置,方便后续保存文件操作

        // 2. 对应格式专项校验
        return fileExt switch
        {
            ".pdf" => ValidatePdfFormat(headerBuffer),
            ".doc" => ValidateDocFormat(headerBuffer, fileStream),
            ".txt" => ValidateTxtFormat(fileStream),
            _ => false
        };
    }

    // PDF格式校验
    private static bool ValidatePdfFormat(byte[] header)
    {
        return header[0] == 0x25 && header[1] == 0x50 && header[2] == 0x44 && header[3] == 0x46 && header[4] == 0x2D;
    }

    // DOC格式校验
    private static bool ValidateDocFormat(byte[] header, Stream fileStream)
    {
        // 先校验OLE复合文档通用头
        var oleHeader = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
        if (!oleHeader.SequenceEqual(header.Take(8))) return false;

        // 额外校验Word专属特征,避免是XLS/PPT改扩展名
        fileStream.Position = 0;
        using var reader = new StreamReader(fileStream, Encoding.Default, true);
        string previewContent = reader.ReadToEnd(1024); // 仅读取前1K内容找特征
        fileStream.Position = 0;
        return previewContent.Contains("Word.Document");
    }

    // TXT格式校验
    private static bool ValidateTxtFormat(Stream fileStream)
    {
        fileStream.Position = 0;
        // 排除EXE/DLL等PE格式文件的MZ头
        if (fileStream.ReadByte() == 0x4D && fileStream.ReadByte() == 0x5A)
        {
            fileStream.Position = 0;
            return false;
        }

        // 排除脚本类特征
        fileStream.Position = 0;
        using var reader = new StreamReader(fileStream, Encoding.UTF8, true);
        string previewContent = reader.ReadToEnd(4096); // 仅读取前4K内容检测
        fileStream.Position = 0;
        var forbiddenFeatures = new[] { "<?php", "<%", "<script", "eval(", "exec(", "system(" };
        return !forbiddenFeatures.Any(f => previewContent.IndexOf(f, StringComparison.OrdinalIgnoreCase) >= 0);
    }
}

生产环境优化建议

  • DOC格式校验建议替换为NPOI、OpenXML SDK等专业库尝试解析文件,能正常解析才判定为合法,避免特征串被绕过
  • TXT格式可新增编码合法性检测,确认文件符合UTF8/GBK等常见文本编码规则,排除二进制文件
  • 上传后的文件不要存储在Web可访问目录下,存储时重命名为随机无意义文件名,不保留原始扩展名,用户下载时通过接口动态设置Content-Disposition返回正确文件名
  • 有条件的场景可接入病毒扫描引擎,对上传文件做二次查杀
  • 上传接口需配置频率限制,避免恶意批量上传攻击

内容的提问来源于stack exchange,提问作者Arumugam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 18:54:05