erbasaran/SmartFileKit.NET
GitHub: erbasaran/SmartFileKit.NET
一个零依赖的 .NET 库,通过魔数校验、MIME 检测和安全分析引擎来解决文件上传过程中的类型伪造与安全风险问题。
Stars: 0 | Forks: 0
# SmartFileKit
**SmartFileKit** 是一个**轻量级**、**高性能**且**注重安全**的 .NET 库,专为**安全文件验证**、**文件签名(魔数)校验**、**MIME 类型检测**和**文件分类**而设计。它以**可靠性**、**高性能**和**零外部依赖**为核心理念构建,是**企业级应用**和**安全文件上传场景**的理想选择。
[](https://www.nuget.org/packages/SmartFileKit)
[](LICENSE)
## 🚀 核心特性
- **文件大小格式化与计算:** 动态将字节转换为 KB、MB、GB 和 TB 单位,执行算术运算和比较,并使用 `CultureInfo.InvariantCulture` 以可配置的小数精度格式化输出。
- **MIME 与类别检测:** 自动识别超过 40 种流行文件格式的 MIME 类型和高级类别(`Image`、`Document`、`Spreadsheet`、`Presentation`、`Archive`、`Video`、`Audio`、`Executable` 等)。
- **基于内容的魔数校验:** 检查文件头部的起始字节(最多 4096 字节),而不是仅仅依赖文件扩展名。这可以防止扩展名伪造攻击(例如,检测伪装成 `.jpg` 的 `.exe` 二进制文件,或重命名为 `.docx` 的任意 ZIP 包)。
- **文件名净化:** 通过将土耳其语字符转换为对应的英文字符、移除无效的文件系统字符、缓解目录遍历攻击,以及防止 Windows 保留设备名冲突(`CON`、`PRN`、`NUL` 等),来清理用户提供的文件名。
- **流式验证 API:** 一种高可读性的构建器语法,用于设置大小范围、允许/阻止的扩展名、允许/阻止的 MIME 类型、允许/阻止的类别以及激活的签名验证。
- **高级文件分析与安全引擎:** 动态分析文件以生成详细报告,其中包含风险评分(0-100)、风险等级、威胁问题列表、Office 文件结构验证(将 ZIP 细分为 DOCX/XLSX/PPTX)、ZIP 归档内部可执行文件扫描、多格式文件(多重签名)检测以及 Shannon 熵计算。
- **零依赖与低分配:** 完全独立的库,利用数组和流缓冲池最大化大规模 ASP.NET Core API 的吞吐量。
## 📦 安装
通过 .NET CLI 安装:
```
dotnet add package SmartFileKit
```
或者通过 Package Manager Console 安装:
```
Install-Package SmartFileKit
```
## 🛠️ 使用示例
### 1. 文件大小格式化与扩展
`FileSize` 结构体支持比较和算术运算符,并且十进制格式设置是不依赖于区域性的(始终使用点号 `.`)。
```
using SmartFileKit;
using SmartFileKit.Domain;
// Define sizes fluently
FileSize maxLimit = 5.MB();
FileSize minLimit = 500.KB();
// Alternative static creation methods
FileSize size1 = FileSize.FromBytes(2048);
FileSize size2 = FileSize.FromKilobytes(1.5);
FileSize size3 = FileSize.FromMegabytes(10);
FileSize size4 = FileSize.FromGigabytes(2.5);
FileSize size5 = FileSize.FromTerabytes(1);
// Convert numeric types to FileSize
long bytesCount = 1048576 * 3; // 3 MB
FileSize size = bytesCount.ToFileSize();
Console.WriteLine(size.ToString()); // Output: "3.00 MB" (Default precision is 2)
Console.WriteLine(size.ToString(1)); // Output: "3.0 MB"
// Comparisons and arithmetic (full support for +, -, *, /, <, >, <=, >=, ==, !=)
if (size < maxLimit)
{
FileSize totalSpace = size + 10.MB();
FileSize scaled = minLimit * 3;
FileSize halved = maxLimit / 2;
Console.WriteLine($"Total Space: {totalSpace}"); // Output: "13.00 MB"
}
```
### 2. 文件名净化与目录遍历保护
在将名称保存到磁盘之前对其进行规范化。这会转换特殊字符、剥离路径操纵技巧,并避免占用操作系统保留的文件名。
```
using SmartFileKit;
// Turkish character normalization
string fileName1 = FileName.Sanitize("özel rapor (2026).pdf");
// Output: "ozel rapor (2026).pdf"
// Directory traversal protection
string fileName2 = FileName.Sanitize("../../../etc/passwd");
// Output: "etc_passwd"
string fileName3 = FileName.Sanitize("..\\..\\windows\\system32.dll");
// Output: "windows_system32.dll"
// Windows Reserved name resolution
string fileName4 = FileName.Sanitize("CON.txt");
// Output: "_CON.txt"
```
### 3. MIME 与类别检测
使用 `Stream`、`byte[]` 或 `string` 文件路径检测真实的文件格式。支持查找的流在签名检查后会自动倒回其原始位置。
```
using System.IO;
using SmartFileKit;
using SmartFileKit.Domain;
using SmartFileKit.Detection;
// Detection from Stream
using (var stream = File.OpenRead("upload.dat"))
{
FileFormatInfo format = FileType.GetFormat(stream);
if (format != null)
{
Console.WriteLine($"Extension: {format.Extension}"); // e.g. ".jpg"
Console.WriteLine($"MIME: {format.MimeType}"); // e.g. "image/jpeg"
Console.WriteLine($"Category: {format.Category}"); // e.g. "Image"
}
// Direct helper checks (also support byte[] and file path)
bool isImage = FileType.IsImage(stream);
bool isArchive = FileType.IsArchive(stream);
}
// Detection from file path or byte array
FileFormatInfo formatFromPath = FileType.GetFormat("C:\\uploads\\document.pdf");
bool isDoc = FileType.IsDocument(new byte[] { 0x25, 0x50, 0x44, 0x46 }); // PDF signature
bool isVideo = FileType.IsVideo("C:\\uploads\\video.mp4"); // filepath
// Available categories in FileCategory enum:
// Unknown, Image, Document, Spreadsheet, Presentation, Archive, Audio, Video, Executable, Web, Data
// Dynamic MIME/Category Mapping Lookup using MimeMapper
string mime = MimeMapper.GetMimeType(".jpg"); // "image/jpeg"
string ext = MimeMapper.GetExtension("image/png"); // ".png"
FileCategory cat1 = MimeMapper.GetCategory(".pdf"); // FileCategory.Document
FileCategory cat2 = MimeMapper.GetCategoryByMimeType("application/zip"); // FileCategory.Archive
```
### 4. 流式文件验证
在文件上传期间强制执行安全性和约束条件。默认情况下会启用伪造检查,将扩展名声明与实际标头进行比较。
```
using System;
using SmartFileKit.Validation;
using SmartFileKit.Domain;
byte[] fileData = new byte[] { 0x00, 0x00, 0x00, 0x00 }; // E.g., user upload
// Validate can accept: Stream, byte[], string file path, or FileInfo
var result = FileValidator.Validate(fileData, "avatar.jpg")
.MaxSize(5.MB())
.MinSize(10.KB())
.AllowedExtensions(".jpg", ".png")
.BlockedExtensions(".gif")
.AllowedMimeTypes("image/jpeg", "image/png")
.BlockedMimeTypes("image/gif")
.AllowedCategories(FileCategory.Image)
.BlockedCategories(FileCategory.Executable)
.VerifySignature(true) // Verify magic bytes (default: true)
.AllowEmpty(false) // Allow empty files (default: false)
.Execute();
if (!result.IsValid)
{
Console.WriteLine("Validation Failed!");
foreach (var error in result.Errors)
{
Console.WriteLine($"- {error}");
}
}
// Or throw a FileValidationException directly:
try
{
FileValidator.Validate(fileData, "avatar.jpg")
.MaxSize(5.MB())
.AllowedExtensions(".jpg")
.ThrowIfInvalid();
}
catch (FileValidationException ex)
{
Console.WriteLine(ex.Message);
}
```
### 5. 可重用的上传安全策略
以线程安全的方式定义一次标准安全策略,并根据这些策略验证不同的文件流。
```
using System.IO;
using SmartFileKit.Security;
using SmartFileKit.Domain;
using SmartFileKit.Validation;
// Define a reusable security policy
public static class SecurityPolicies
{
public static readonly UploadPolicy WebImagePolicy = UploadPolicy.Create()
.MaxSize(10.MB())
.AllowImages() // Pre-defined helper: PNG, JPG, JPEG, GIF, WEBP, BMP, SVG
.BlockExtensions(".exe", ".dll", ".bat") // Extra safety
.EnforceSignature(true); // Verify binary signature (magic bytes)
}
// Validate uploads against the policy in controllers / handlers
using (var stream = File.OpenRead("user_avatar.png"))
{
FileValidationResult result = SecurityPolicies.WebImagePolicy.Validate(stream, "user_avatar.png");
if (!result.IsValid)
{
Console.WriteLine("Upload policy violated!");
}
}
```
### 6. 高级文件分析与安全引擎
动态分析上传的文件,以执行签名检查、ZIP/Office 文件的结构检查、多格式检测、熵计算,并生成安全风险评分。
```
using System.IO;
using SmartFileKit.Analysis;
using SmartFileKit.Domain;
// Simple analysis
using (var stream = File.OpenRead("invoice.pdf"))
{
FileAnalysisReport report = FileAnalyzer.Analyze(stream, "invoice.pdf");
Console.WriteLine($"Is Safe: {report.IsSafe}");
Console.WriteLine($"Is Suspicious: {report.IsSuspicious}");
Console.WriteLine($"Risk Score: {report.RiskScore} / 100");
Console.WriteLine($"Risk Level: {report.RiskLevel}");
Console.WriteLine($"Actual Type: {report.ActualFileType}"); // e.g. "pdf"
foreach (var issue in report.Issues)
{
Console.WriteLine($"- Warning: {issue.Type} - {issue.Description} ({issue.Severity})");
}
}
// Advanced fluent configuration
using (var stream = File.OpenRead("uploaded.dat"))
{
FileAnalysisReport report = FileAnalyzer.Create()
.ValidateSignature(true)
.ValidateMime(true)
.ValidateStructure(true)
.CalculateRiskScore(true)
.CheckEntropy(true, threshold: 7.5)
.Analyze(stream, "data.txt", "text/plain");
if (report.IsSuspicious)
{
Console.WriteLine($"Suspicious file detected! Entropy: {report.Entropy}");
}
}
```
### 7. 加密哈希与重复文件检测
#### 加密哈希
使用低开销算法为流和字节数组生成哈希。
```
using System.IO;
using SmartFileKit.Security;
// Hashing streams (MD5, SHA1, SHA256, SHA512)
using (var stream = File.OpenRead("document.pdf"))
{
string sha256 = FileHash.Sha256(stream);
string md5 = FileHash.Calculate(stream, HashAlgorithmType.MD5);
}
```
#### 文件指纹识别
将物理和语义特征组合到单个 `FileFingerprint` 中,以实现统一识别。
```
using System.IO;
using SmartFileKit.Security;
using (var stream = File.OpenRead("document.pdf"))
{
FileFingerprint fingerprint = FileFingerprint.Generate(stream, "document.pdf");
Console.WriteLine($"Size: {fingerprint.Size} bytes");
Console.WriteLine($"SHA256: {fingerprint.Sha256}");
Console.WriteLine($"MIME Type: {fingerprint.MimeType}");
Console.WriteLine($"Signature Type: {fingerprint.SignatureType}"); // e.g. "pdf"
Console.WriteLine($"Category: {fingerprint.Category}"); // e.g. Document
}
```
#### 重复文件检测
逐字节比较文件,或通过预先计算的指纹和哈希进行比较。
```
using System.IO;
using SmartFileKit.Security;
using (var s1 = File.OpenRead("file1.dat"))
using (var s2 = File.OpenRead("file2.dat"))
{
// Fast, byte-by-byte comparison of streams (resets positions automatically)
bool isDuplicateStream = DuplicateDetector.AreSame(s1, s2);
// Compare via pre-computed fingerprints
var fp1 = FileFingerprint.Generate(s1);
var fp2 = FileFingerprint.Generate(s2);
bool isDuplicateFingerprint = DuplicateDetector.AreSame(fp1, fp2);
// Hash string comparison helper
bool isDuplicateHash = DuplicateDetector.Compare(fp1.Sha256, fp2.Sha256);
}
```
### 8. 文件名风险与安全分析
进行扩展名验证、双重扩展名检查以及文件名安全扫描(目录遍历、保留字、Shell 字符)。
```
using SmartFileKit.Security;
// Dangerous extension check
bool isDangerous = FileSecurity.IsDangerousExtension("setup.exe"); // true
// Double extension verification (invoice.pdf.exe)
DoubleExtensionResult doubleExt = FileSecurity.HasDoubleExtension("invoice.pdf.exe");
if (doubleExt.HasDoubleExtension)
{
Console.WriteLine($"Disguised extension: {doubleExt.SecondExtension} (Dangerous: {doubleExt.IsDangerous})");
}
// Filename risk scan
FileNameAnalysisResult riskReport = FileSecurity.AnalyzeFileName("../CON.txt");
if (!riskReport.IsSafe)
{
Console.WriteLine($"Threat Score: {riskReport.RiskScore} / 100");
foreach (var issue in riskReport.Issues)
{
Console.WriteLine($"- {issue}");
}
}
```
### 9. 图像与 Office 元数据读取器
无需外部依赖即可从图像和 Office 文件中提取元数据。
```
using SmartFileKit.Security;
// Image dimensions (zero dependency)
using (var stream = File.OpenRead("photo.jpg"))
{
ImageMetadataResult meta = ImageMetadata.Read(stream);
if (meta.IsValid)
{
Console.WriteLine($"Image: {meta.Width}x{meta.Height} ({meta.Format})");
}
}
// Office properties (author, title, creation, company)
using (var stream = File.OpenRead("proposal.docx"))
{
OfficeMetadataResult meta = OfficeMetadata.Read(stream);
if (meta.IsValid)
{
Console.WriteLine($"Author: {meta.Author}, Company: {meta.Company}");
}
}
```
## 🛡️ 安全最佳实践
1. **永远不要信任扩展名:** 用户可以轻易地将恶意的 `.exe` 文件重命名为 `.jpg`。SmartFileKit 会检查二进制标头(`VerifySignature` 选项)以确定真实的文件格式。在生产环境中务必始终验证签名。
2. **净化输出名称:** 在将上传内容写入磁盘时,不要直接使用原始的 `FileName` 标头。始终执行 `FileName.Sanitize` 以防范路径遍历攻击。
## ⚡ 性能优化
- **快速的标头扫描:** 签名引擎仅扫描文件的**前 4096 字节**,即使是多 GB 的上传量,也能将执行时间保持在毫秒级以内。
- **可倒回的流:** 如果输入的 `Stream` 支持查找,当检测结束时,其指针将重置为起始位置。这确保了连续的写入操作或下游处理程序能够接收到完整无损的流。
## 📄 许可证
本项目基于 **MIT License** 授权。
标签:MIME检测, 多人体追踪, 数据清洗, 文件安全, 文件校验, 文件类型识别