赞
踩
向用户提供向服务器上传文件的功能时,必须格外小心。 攻击者可能会尝试执行以下操作:
降低成功攻击可能性的安全措施如下:
常见的文件存储选项有:
文件上传方案
缓冲和流式传输是上传文件的两种常见方法。
缓冲
整个文件将读入一个 IFormFile。 IFormFile 是用于处理或保存文件的 C# 表示形式。
文件上传使用的磁盘和内存取决于并发文件上传的数量和大小。 如果应用尝试缓冲过多上传,站点就会在内存或磁盘空间不足时崩溃。 如果文件上传的大小或频率会消耗应用资源,请使用流式传输。
会将大于 64 KB 的所有单个缓冲文件从内存移到磁盘的临时文件。
用于较大请求的 ASPNETCORE_TEMP 临时文件将写入环境变量中命名的位置。 如果未 ASPNETCORE_TEMP 定义,文件将写入当前用户的临时文件夹。
本主题的以下部分介绍了如何缓冲小型文件:
流式处理
从多部分请求收到文件,然后应用直接处理或保存它。 流式传输无法显著提高性能。 流式传输可降低上传文件时对内存或磁盘空间的需求。
前端使用FormData进行实现批量上传
<!doctype html> <html> <head> <meta charset="utf-8"> <title>上传</title> </head> <form method="post" id="uploadForm" enctype="multipart/form-data"> <input type="file" name="file" multiple /> <input type="button" value="上传" onclick="doUpload()" /> </form> <body> <script src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script> $(function () { }); function doUpload() { var formData = new FormData($("#uploadForm")[0]); $.ajax({ url: 'http://localhost:5000/api/Path/Upload', type: 'post', data: formData, async: false, cache: false, contentType: false, processData: false, success: function (returndata) { console.dir(returndata); }, error: function (returndata) { console.dir(returndata); } }) } </script>
批量上传选择多个文件:
后端.Net Core 使用 IFormFile 强类型灵活绑定获取文件信息
/// <summary> /// 文件上传 /// </summary> /// <returns></returns> [HttpPost] public MethodResult Upload([FromForm(Name = "file")] List<IFormFile> files) { files.ForEach(file => { var fileName = file.FileName; string fileExtension = file.FileName.Substring(file.FileName.LastIndexOf(".") + 1);//获取文件名称后缀 //保存文件 var stream = file.OpenReadStream(); // 把 Stream 转换成 byte[] byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); // 设置当前流的位置为流的开始 stream.Seek(0, SeekOrigin.Begin); // 把 byte[] 写入文件 FileStream fs = new FileStream("D:\" + file.FileName, FileMode.Create); BinaryWriter bw = new BinaryWriter(fs); bw.Write(bytes); bw.Close(); fs.Close(); }); return new MethodResult("success", 1); }
上传后文件:
[HttpPost, Route("UploadFile")] public JsonActionResult<bool> UploadFileCallDifferentServerWebApi() { JsonActionResult<bool> jsonActionResult = new JsonActionResult<bool>(); var client = new RestClient("https://localhost:44302/Maint/SysFile/Upload"); client.Timeout = -1; var request = new RestRequest(Method.POST); request.AddHeader("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJDLL_Gc"); request.AddHeader("Content-Type", "multipart/form-data"); var httpRequest = System.Web.HttpContext.Current.Request; foreach (string key in httpRequest.Files) // 文件键 { var postedFile = httpRequest.Files[key]; // 获取文件键对应的文件对象 Byte[] fileData = new Byte[postedFile.ContentLength]; Stream sr = postedFile.InputStream;//创建数据流对象 sr.Read(fileData, 0, postedFile.ContentLength); request.AddFile("fileInput", fileData, key); } request.AddParameter("pageCode", "jlysFile"); var response = client.Execute(request); //string str = string.Empty; //var httpRequest = System.Web.HttpContext.Current.Request; //foreach (string key in httpRequest.Files) // 文件键 //{ // var postedFile = httpRequest.Files[key]; // 获取文件键对应的文件对象 // Byte[] fileData = new Byte[postedFile.ContentLength]; // Stream sr = postedFile.InputStream;//创建数据流对象 // sr.Read(fileData, 0, postedFile.ContentLength); // str = System.Text.Encoding.UTF8.GetString(fileData); //} //result = HttpManager.HttpPostCarryDefaultToken<WebResponseContent>("https://localhost:44302/Maint/SysFile/Upload", str, "multipart/form-data"); return jsonActionResult; }
将第三方病毒/恶意软件扫描 API 用于上传的内容。
在大容量方案中,在服务器资源上扫描文件较为困难。 若文件扫描导致请求处理性能降低,请考虑将扫描工作卸载到后台服务,该服务可以是在应用服务器之外的服务器上运行的服务。 通常会将卸载的文件保留在隔离区,直至后台病毒扫描程序检查它们。 文件通过检查时,会将相应的文件移到常规的文件存储位置。 通常在执行这些步骤的同时,会提供指示文件扫描状态的数据库记录。 通过此方法,应用和应用服务器可以持续以响应请求为重点。
应在允许的扩展名列表中查找上传的文件的扩展名。 例如:
private string[] permittedExtensions = { ".txt", ".pdf" };
var ext = Path.GetExtension(uploadedFileName).ToLowerInvariant();
if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
{
// The extension is invalid ... discontinue processing the file
}
文件的签名由文件开头部分中的前几个字节确定。 可以使用这些字节指示扩展名是否与文件内容匹配。 示例应用检查一些常见文件类型的文件签名。 在下面的示例中,在文件上检查 JPEG 图像的文件签名:
private static readonly Dictionary<string, List<byte[]>> _fileSignature = new Dictionary<string, List<byte[]>> { { ".jpeg", new List<byte[]> { new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 }, } }, }; using (var reader = new BinaryReader(uploadedFileData)) { var signatures = _fileSignature[ext]; var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length)); return signatures.Any(signature => headerBytes.Take(signature.Length).SequenceEqual(signature)); }
切勿使用客户端提供的文件名来将文件保存到物理存储。 使用 Path.GetRandomFileName 或 Path.GetTempFileName 为文件创建安全的文件名,以创建完整路径(包括文件名)来执行临时存储。
Razor 自动对属性值执行 HTML 编码以进行显示。 以下代码安全可用:
@foreach (var file in Model.DatabaseFiles) {
<tr>
<td>
@file.UntrustedName
</td>
</tr>
}
限制上传的文件的大小。
在示例应用中,文件大小限制为 2 MB(以字节为单位)。 通过 appsettings.json 文件中的配置来提供此限制:
{
"FileSizeLimit": 2097152
}
将 FileSizeLimit 注入到 PageModel 类:
public class BufferedSingleFileUploadPhysicalModel : PageModel
{
private readonly long _fileSizeLimit;
public BufferedSingleFileUploadPhysicalModel(IConfiguration config)
{
_fileSizeLimit = config.GetValue<long>("FileSizeLimit");
}
...
}
文件大小超出限制时,将拒绝文件:
if (formFile.Length > _fileSizeLimit)
{
// The file is too large ... discontinue processing the file
}
在发布窗体数据或直接使用 JavaScript 的 FormData 的非 Razor 窗体中,窗体元素或 FormData 中指定的名称必须与控制器操作中的参数名称相匹配。
在以下示例中
使用 <input>
元素时,将 name 属性设置为值 battlePlans:
<input type="file" name="battlePlans" multiple>
使用 JavaScript FormData 时,将名称设置为值 battlePlans:
var formData = new FormData();
for (var file in files) {
formData.append("battlePlans", file, file.name);
}
将匹配的名称用于 C# 方法的参数 (battlePlans)
对于名为 Upload 的 Razor Pages 页面处理程序方法:
public async Task<IActionResult> OnPostUploadAsync(List<IFormFile> battlePlans)
对于 MVC POST 控制器操作方法:
public async Task<IActionResult> Post(List<IFormFile> battlePlans)
.NET Core Web APi FormData多文件上传,IFormFile强类型文件灵活绑定
在 ASP.NET Core 中上传文件
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。