C#文件上传MIME类型验证 C#如何根据文件头判断真实文件类型

来源:这里教程网 时间:2026-02-21 17:41:40 作者:

为什么不能只靠文件扩展名或Content-Type做验证

浏览器传来的

Content-Type
(即 MIME 类型)完全由客户端决定,可任意伪造;扩展名更不可信。攻击者只要把恶意可执行文件改成
.jpg
就能绕过纯后缀检查。真实类型必须从文件字节头(magic number)判断。

常见误操作包括:

仅用
Path.GetExtension(fileName)
匹配白名单
直接信任
IFormFile.ContentType
读取整个文件再分析(浪费内存、阻塞 I/O)

如何用 C# 读取前几个字节判断文件类型

核心思路:只读取前 16–32 字节(足够覆盖绝大多数 magic header),比对已知签名。不要加载全文件。

关键点:

使用
IFormFile.OpenReadStream()
获取流,避免内存拷贝
调用
stream.ReadAsync(buffer, 0, buffer.Length)
限制长度(如 32 字节)
Memory<byte></byte>
Span<byte></byte>
做无分配比对
注意 PNG/JPEG/GIF 等格式的 signature 位置和长度(如 JPEG 是
FF D8 FF
开头,PNG 是
89 50 4E 47

示例片段(简化逻辑):

byte[] header = new byte[32];
await file.OpenReadStream().ReadAsync(header, 0, header.Length);
if (header.AsSpan().StartsWith(new byte[] { 0xFF, 0xD8, 0xFF })) {
    // JPEG
} else if (header.AsSpan().StartsWith(new byte[] { 0x89, 0x50, 0x4E, 0x47 })) {
    // PNG
}

有哪些现成可靠的 MIME 推断库可直接用

自己维护 magic number 表容易漏判、难覆盖边缘格式(如 WebP、AVIF、Office 文档)。推荐两个轻量方案:

FileTypeDetector
(NuGet 包
FileTypeDetector
):专注 header 检测,无依赖,支持 100+ 类型,API 极简:

var detector = new FileTypeDetector();
using var stream = file.OpenReadStream();
var result = await detector.DetectFileTypeAsync(stream); // 返回 MimeType 和 Confidence
if (result.MimeType == "image/jpeg" && result.Confidence > 0.9) { ... }

Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream
配合自定义检测器:适合需要深度控制流生命周期的场景,但需自行管理 buffer 复位(
stream.Position = 0
后才能传给后续处理)

注意:别用

System.Drawing.Common
加载图片验证——它不校验 header,且在非 Windows 环境可能崩溃,还吃内存。

验证失败时该返回什么错误码和提示

HTTP 层应返回明确语义的状态码,而非笼统的 400:

415 Unsupported Media Type
:MIME 不在允许列表内(如传了
application/x-executable
422 Unprocessable Entity
:文件头与扩展名冲突(如
.pdf
但 header 是 JPEG)
日志中必须记录原始
ContentType
、检测出的 MIME、文件名、header 前 8 字节十六进制(用于事后审计)

切忌只返回“文件类型不合法”这种模糊提示——前端无法据此友好提示用户,也掩盖了真实攻击尝试。

文件头验证不是银弹。ZIP 类压缩包、加密 PDF、带元数据的 TIFF 都可能绕过简单 signature 检查。真要防住高级攻击,得配合沙箱解析或服务端反病毒扫描。

相关推荐