From e06d4f8f5191ed0dd4b7ec148aba0166a54246a8 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:46:16 -0400 Subject: [PATCH 1/5] Request Streaming upload --- aspnetcore/blazor/file-uploads.md | 34 +++++++++++++++---- .../aspnetcore-9/includes/blazor.md | 6 ++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index b713b2747872..3c2826848bcf 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -101,26 +101,46 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof ## File size read and upload limits -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-9.0" + +Server-side or client-side, there's no file read or upload size limit for the component. + +For Chromium-based browsers (Google Chrome and Microsoft Edge) using the HTTP/2 protocol, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (> 2 GB or larger than the device's available memory). + +For non-Chromium browsers or without HTTP/2 protocol, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: + +* [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) +* [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) + +For large client-side file uploads that fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-9.0" Server-side or client-side, there's no file read or upload size limit specifically for the component. However, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: +* [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) +* [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) + +For large client-side file uploads that fail when attempting to use the component, we recommend: + +* Adopting .NET 9 or later for Chromium-based browsers (Google Chrome and Microsoft Edge) with HTTP/2 protocol support, where this problem is internally addressed by Blazor with [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API). +* For non-Chromium browsers, Chromium browsers without HTTP/2, or .NET 8 or earlier, chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. + :::moniker-end :::moniker range="< aspnetcore-6.0" The maximum supported file size for the component is 2 GB. Additionally, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: -:::moniker-end - * [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) * [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) -For large client-side file uploads that fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. +* Adopting .NET 9 or later for Chromium-based browsers (Google Chrome and Microsoft Edge) with HTTP/2 protocol support, where this problem is internally addressed by Blazor with [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API). +* For non-Chromium browsers, Chromium browsers without HTTP/2, or .NET 5 or earlier, chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. - - -Work is currently scheduled for .NET 9 (late 2024) to address the client-side file size upload limitation. +:::moniker-end ## Examples diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index ebe19d8ee6e8..75a7fc41b30b 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -235,3 +235,9 @@ The component now s Support for multiple Blazor Web Apps per server project will be considered for .NET 10 (November, 2025). For more information, see [Support for multiple Blazor Web apps per server project (`dotnet/aspnetcore` #52216)](https://github.com/dotnet/aspnetcore/issues/52216). + +### File size read and upload limits + +For Chromium-based browsers (Google Chrome and Microsoft Edge) using the HTTP/2 protocol, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (> 2 GB or larger than the device's available memory). + +For more information, see . From ec9d5afa3486bcb05c6d4650530fc21c5bb4cb77 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 25 Sep 2024 07:56:21 -0400 Subject: [PATCH 2/5] Updates --- aspnetcore/blazor/call-web-api.md | 47 +++- aspnetcore/blazor/file-uploads.md | 264 ++++++++++++++++-- .../aspnetcore-9/includes/blazor.md | 9 +- 3 files changed, 288 insertions(+), 32 deletions(-) diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index 258645289e62..de3286db6d51 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -5,7 +5,7 @@ description: Learn how to call a web API from Blazor apps. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 03/08/2024 +ms.date: 09/25/2024 uid: blazor/call-web-api --- # Call a web API from ASP.NET Core Blazor @@ -242,6 +242,37 @@ You can address this by flowing prerendered state using the Persistent Component :::moniker-end +:::moniker range=">= aspnetcore-9.0" + +## Client-side request streaming + +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests). + +To enable request streaming, set to `true` on the . + +In the following file upload example: + +* `content` is the file's . +* `/Filesave` is the web API endpoint. +* `Http` is the . + +```csharp +var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave"); +request.SetBrowserRequestStreamingEnabled(true); +request.Content = content; + +var response = await Http.SendAsync(request); +``` + +Streaming requests: + +* Require HTTPS protocol and don't work on HTTP/1.x. +* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) is required, and CORS preflight request is always issued. + +For more information on file uploads with an component, see and the example at [Upload files to a server with client-side rendering (CSR)](xref:blazor/file-uploads#upload-files-to-a-server-with-client-side-rendering-csr). + +:::moniker-end + ## Add the `HttpClient` service *The guidance in this section applies to client-side scenarios.* @@ -257,10 +288,7 @@ In the `Program` file, add an service if it is ```csharp builder.Services.AddScoped(sp => - new HttpClient - { - BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) - }); + new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); ``` The preceding example sets the base address with `builder.HostEnvironment.BaseAddress` (), which gets the base address for the app and is typically derived from the `` tag's `href` value in the host page. @@ -274,10 +302,7 @@ If you're calling an external web API (not in the same URL space as the client a ```csharp builder.Services.AddScoped(sp => - new HttpClient - { - BaseAddress = new Uri("https://localhost:5001") - }); + new HttpClient { BaseAddress = new Uri("https://localhost:5001") }); ``` ## JSON helpers @@ -524,7 +549,7 @@ If you're calling an external web API (not in the same URL space as the client a ```csharp builder.Services.AddHttpClient("WebAPI", client => - client.BaseAddress = new Uri(https://localhost:5001)); + client.BaseAddress = new Uri("https://localhost:5001")); ``` In the following component code: @@ -624,7 +649,7 @@ If you're calling an external web API (not in the same URL space as the client a ```csharp builder.Services.AddHttpClient(client => - client.BaseAddress = new Uri(https://localhost:5001)); + client.BaseAddress = new Uri("https://localhost:5001")); ``` Components inject the typed to call the web API. diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index 3c2826848bcf..fdc8f2395f11 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -5,7 +5,7 @@ description: Learn how to upload files in Blazor with the InputFile component. monikerRange: '>= aspnetcore-5.0' ms.author: riande ms.custom: mvc -ms.date: 08/29/2024 +ms.date: 09/25/2024 uid: blazor/file-uploads --- # ASP.NET Core Blazor file uploads @@ -105,14 +105,14 @@ If you're using the [Autofac Inversion of Control (IoC) container](https://autof Server-side or client-side, there's no file read or upload size limit for the component. -For Chromium-based browsers (Google Chrome and Microsoft Edge) using the HTTP/2 protocol, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (> 2 GB or larger than the device's available memory). +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, HTTPS, and [CORS](xref:security/cors), client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (over 250 MB or larger than the device's available memory) with [request streaming](xref:blazor/call-web-api#client-side-request-streaming). -For non-Chromium browsers or without HTTP/2 protocol, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads (> 250 MB) may fail for client-side uploads using the component. For more information, see the following discussions: +For non-Chromium browsers or without HTTP/2 protocol/HTTPS/CORS, client-side Blazor reads the file's bytes into a single JavaScript array buffer when marshalling the data from JavaScript to C#, which is limited to 2 GB or to the device's available memory. Large file uploads, typically 250 MB or larger, may fail for client-side uploads using the component. For more information, see the following discussions: * [The Blazor InputFile Component should handle chunking when the file is uploaded (dotnet/runtime #84685)](https://github.com/dotnet/runtime/issues/84685) * [Request Streaming upload via http handler (dotnet/runtime #36634)](https://github.com/dotnet/runtime/issues/36634) -For large client-side file uploads that fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. +For large client-side file uploads that don't meet the preceding criteria for request streaming and fail when attempting to use the component, we recommend chunking large files with a custom component using multiple [HTTP range requests](https://developer.mozilla.org/docs/Web/HTTP/Range_requests) instead of using the component. :::moniker-end @@ -356,7 +356,7 @@ The following `FileUpload2` component: :::moniker range=">= aspnetcore-8.0" -If the component limits file uploads to a single file at a time or if the component only adopts interactive client-side rendering (CSR, `InteractiveWebAssembly`), the component can avoid the use of the `LazyBrowserFileStream` and use a . The following demonstrates the changes for the `FileUpload2` component: +If the component limits file uploads to a single file at a time or if the component only adopts client-side rendering (CSR, `InteractiveWebAssembly`), the component can avoid the use of the `LazyBrowserFileStream` and use a . The following demonstrates the changes for the `FileUpload2` component: ```diff - var stream = new LazyBrowserFileStream(file, maxFileSize); @@ -508,6 +508,12 @@ The server app must register controller services and map controller endpoints. F The following example demonstrates uploading files to a backend web API controller in a separate app, possibly on a separate server, from a component in a Blazor Web App that adopts CSR or a component in a Blazor WebAssembly app. +:::moniker range=">= aspnetcore-9.0" + +The example adopts [request streaming](xref:blazor/call-web-api#client-side-request-streaming) for a Chromium-based browser (for example, Google Chrome or Microsoft Edge) with HTTP/2 protocol, [CORS](xref:security/cors), and HTTPS. If request streaming can't be used, Blazor gracefully degrades to [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) without request streaming. For more information, see the [File size read and upload limits](#file-size-read-and-upload-limits) section. + +:::moniker-end + The following `UploadResult` class maintains the result of an uploaded file. When a file fails to upload on the server, an error code is returned in `ErrorCode` for display to the user. A safe file name is generated on the server for each file and returned to the client in `StoredFileName` for display. Files are keyed between the client and server using the unsafe/untrusted file name in `FileName`. `UploadResult.cs`: @@ -546,15 +552,15 @@ A security best practice for production apps is to avoid sending error messages :::moniker range=">= aspnetcore-8.0" -In the Blazor Web App main project, add and related services in the project's `Program` file: +In the Blazor Web App server project, add and related services in the project's `Program` file: ```csharp builder.Services.AddHttpClient(); ``` -The `HttpClient` services must be added to the main project because the client-side component is prerendered on the server. If you [disable prerendering for the following component](xref:blazor/components/render-modes#prerendering), you aren't required to provide the `HttpClient` services in the main app and don't need to add the preceding line to the main project. +The services must be added to the server project because the client-side component is prerendered on the server. If you [disable prerendering for the following component](xref:blazor/components/render-modes#prerendering), you aren't required to provide the services in the server project and don't need to add the preceding line to the server project. -For more information on adding `HttpClient` services to an ASP.NET Core app, see . +For more information on adding services to an ASP.NET Core app, see . The client project (`.Client`) of a Blazor Web App must also register an for HTTP POST requests to a backend web API controller. Confirm or add the following to the client project's `Program` file: @@ -565,17 +571,177 @@ builder.Services.AddScoped(sp => The preceding example sets the base address with `builder.HostEnvironment.BaseAddress` (), which gets the base address for the app and is typically derived from the `` tag's `href` value in the host page. If you're calling an external web API, set the URI to the web API's base address. -Specify the Interactive WebAssembly render mode attribute at the top of the following component in a Blazor Web App: +A standalone Blazor WebAssembly app that uploads files to a separate server web API either uses a [named `HttpClient`](xref:blazor/call-web-api#named-httpclient-with-ihttpclientfactory) or sets the default service registration to point to the web API's endpoint. In the following example where the web API is hosted locally at port 5001, the base address is `https://localhost:5001`: + +```csharp +builder.Services.AddScoped(sp => + new HttpClient { BaseAddress = new Uri("https://localhost:5001") }); +``` + +:::moniker-end + +:::moniker range=">= aspnetcore-9.0" + +In a Blazor Web App, add the namespace to the component's directives: ```razor -@rendermode InteractiveWebAssembly +@using Microsoft.AspNetCore.Components.WebAssembly.Http ``` :::moniker-end `FileUpload2.razor`: -:::moniker range=">= aspnetcore-8.0" +:::moniker range=">= aspnetcore-9.0" + +```razor +@page "/file-upload-2" +@using System.Linq +@using System.Net.Http.Headers +@using System.Net +@inject HttpClient Http +@inject ILogger Logger + +File Upload 2 + +

File Upload Example 2

+ +

+ +

+ +@if (files.Count > 0) +{ +
+
+
    + @foreach (var file in files) + { +
  • + File: @file.Name +
    + @if (FileUpload(uploadResults, file.Name, Logger, + out var result)) + { + + Stored File Name: @result.StoredFileName + + } + else + { + + There was an error uploading the file + (Error: @result.ErrorCode). + + } +
  • + } +
+
+
+} + +@code { + private List files = new(); + private List uploadResults = new(); + private int maxAllowedFiles = 3; + private bool shouldRender; + + protected override bool ShouldRender() => shouldRender; + + private async Task OnInputFileChange(InputFileChangeEventArgs e) + { + shouldRender = false; + long maxFileSize = 1024 * 15; + var upload = false; + + using var content = new MultipartFormDataContent(); + + foreach (var file in e.GetMultipleFiles(maxAllowedFiles)) + { + if (uploadResults.SingleOrDefault( + f => f.FileName == file.Name) is null) + { + try + { + files.Add(new() { Name = file.Name }); + + var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); + + fileContent.Headers.ContentType = + new MediaTypeHeaderValue(file.ContentType); + + content.Add( + content: fileContent, + name: "\"files\"", + fileName: file.Name); + + upload = true; + } + catch (Exception ex) + { + Logger.LogInformation( + "{FileName} not uploaded (Err: 6): {Message}", + file.Name, ex.Message); + + uploadResults.Add( + new() + { + FileName = file.Name, + ErrorCode = 6, + Uploaded = false + }); + } + } + } + + if (upload) + { + var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave"); + request.SetBrowserRequestStreamingEnabled(true); + request.Content = content; + + var response = await Http.SendAsync(request); + + var newUploadResults = await response.Content + .ReadFromJsonAsync>(); + + if (newUploadResults is not null) + { + uploadResults = uploadResults.Concat(newUploadResults).ToList(); + } + } + + shouldRender = true; + } + + private static bool FileUpload(IList uploadResults, + string? fileName, ILogger logger, out UploadResult result) + { + result = uploadResults.SingleOrDefault(f => f.FileName == fileName) ?? new(); + + if (!result.Uploaded) + { + logger.LogInformation("{FileName} not uploaded (Err: 5)", fileName); + result.ErrorCode = 5; + } + + return result.Uploaded; + } + + private class File + { + public string? Name { get; set; } + } +} +``` + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" :::code language="razor" source="~/../blazor-samples/8.0/BlazorSample_WebAssembly/Pages/FileUpload2.razor"::: @@ -615,7 +781,13 @@ Because the example uses the app's [environment](xref:blazor/fundamentals/enviro > [!WARNING] > The example saves files without scanning their contents, and the guidance in this article doesn't take into account additional security best practices for uploaded files. On staging and production systems, disable execute permission on the upload folder and scan files with an anti-virus/anti-malware scanner API immediately after upload. For more information, see . -In the following example, update the shared project's namespace to match the shared project if a shared project is supplying the `UploadResult` class. +In the following example for a hosted Blazor WebAssembly app or where a shared project is used to supply the `UploadResult` class, add the shared project's namespace: + +```csharp +using BlazorSample.Shared; +``` + +We recommend using a namespace for the following controller (for example: `namespace BlazorSample.Controllers`). `Controllers/FilesaveController.cs`: @@ -628,7 +800,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using BlazorSample.Shared; [ApiController] [Route("[controller]")] @@ -723,15 +894,72 @@ public class FilesaveController : ControllerBase In the preceding code, is called to generate a secure file name. Never trust the file name provided by the browser, as an attacker may choose an existing file name that overwrites an existing file or send a path that attempts to write outside of the app. -The server app must register controller services and map controller endpoints. For more information, see . +The server app must register controller services and map controller endpoints. For more information, see . We recommend adding controller services with in order to automatically [mitigate Cross-Site Request Forgery (XSRF/CSRF) attacks](xref:security/anti-request-forgery) for authenticated users. If you merely use , antiforgery isn't enabled automatically. For more information, see . + +:::moniker range=">= aspnetcore-9.0" + +Cross-Origin Requests (CORS) configuration on the server is required for [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests), and a preflight request is always made by the client. In the service configuration of the server's `Program` file (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: + +:::moniker-end - +Configure Cross-Origin Requests (CORS) on the server. In the service configuration of the backend server web API's `Program` file, the following default CORS policy is suitable for testing with the examples in this article. The client makes the local request from port 5003. Change the port number to match the client app port that you're using: + +:::moniker-end + +```csharp +builder.Services.AddCors(options => +{ + options.AddDefaultPolicy( + policy => + { + policy.WithOrigins("https://localhost:5003") + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); +``` + +After calling in the `Program` file, call to add CORS middleware: + +```csharp +app.UseCors(); +``` + +For more information, see . + +:::moniker range=">= aspnetcore-9.0" + +Configure the server's maximum request body size and multipart body length limits if the limits constrain the upload size. + +For the Kestrel server, set (default: 30,000,000 bytes) and (default: 134,217,728 bytes). Set the `maxFileSize` variable in the component and the controller to the same value. + +In the following `Program` file Kestrel configuration (the server project of a Blazor Web App or the backend server web API of a Blazor WebAssembly app), the `{LIMIT}` placeholder is the limit in bytes: + +```csharp +using Microsoft.AspNetCore.Http.Features; + +... + +builder.WebHost.ConfigureKestrel(serverOptions => +{ + serverOptions.Limits.MaxRequestBodySize = {LIMIT}; +}); + +builder.Services.Configure(options => +{ + options.MultipartBodyLengthLimit = {LIMIT}; +}); +``` + +:::moniker-end ## Cancel a file upload diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index 75a7fc41b30b..2d356d4b2044 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -236,8 +236,11 @@ Support for multiple Blazor Web Apps per server project will be considered for . For more information, see [Support for multiple Blazor Web apps per server project (`dotnet/aspnetcore` #52216)](https://github.com/dotnet/aspnetcore/issues/52216). -### File size read and upload limits +### Client-side request streaming -For Chromium-based browsers (Google Chrome and Microsoft Edge) using the HTTP/2 protocol, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit uploading large files (> 2 GB or larger than the device's available memory). +For Chromium-based browsers (for example, Google Chrome and Microsoft Edge) using the HTTP/2 protocol, [CORS](xref:security/cors), and HTTPS, client-side Blazor uses [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) to permit [request streaming](https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests). -For more information, see . +For more information, see the following resources: + +* +* . From 2a0f3395def2d66a5f94d8c30f2da24e95628803 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 16 Oct 2024 09:14:38 -0400 Subject: [PATCH 3/5] Security considerations for file uploads --- aspnetcore/blazor/file-uploads.md | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index fdc8f2395f11..99e082dc063c 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -142,6 +142,48 @@ The maximum supported file size for the to impose a limit on the file size. + + The following approach is ***insecure*** and must be avoided: + +```diff +- var fileContent = new StreamContent(file.OpenReadStream(file.Size)); +``` + +Instead of using the unsafe client-supplied file size, explicitly specify the maximum file size. The following example sets the maximum file size (`maxFileSize`) to 15 K: + +```csharp +long maxFileSize = 1024 * 15; + +... + +var fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); +``` + +### File name security + +Never use a client-supplied file name for saving a file to physical storage. Create a safe file name for the file using or to create a full path (including the file name) for temporary storage. + +Razor automatically HTML encodes property values for display. The following code is safe to use: + +```cshtml +@foreach (var file in Model.DatabaseFiles) { + + + @file.UntrustedName + + +} +``` + +Outside of Razor, always use to safely encode file names from a user's request. + +Many implementations must include a check that the file exists; otherwise, the file is overwritten by a file of the same name. Supply additional logic to meet your app's specifications. + ## Examples The following examples demonstrate multiple file upload in a component. allows reading multiple files. Specify the maximum number of files to prevent a malicious user from uploading a larger number of files than the app expects. allows reading the first and only file if the file upload doesn't support multiple files. From 316bd5ea12cbd8767d5fbb43ee6c6da08216df9c Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 8 Nov 2024 07:28:09 -0500 Subject: [PATCH 4/5] Updates --- aspnetcore/blazor/call-web-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index de3286db6d51..3d378dae475b 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -267,7 +267,7 @@ var response = await Http.SendAsync(request); Streaming requests: * Require HTTPS protocol and don't work on HTTP/1.x. -* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) is required, and CORS preflight request is always issued. +* Include a body but not a `Content-Length` header. [CORS](xref:security/cors) is required, and a CORS preflight request is always issued. For more information on file uploads with an component, see and the example at [Upload files to a server with client-side rendering (CSR)](xref:blazor/file-uploads#upload-files-to-a-server-with-client-side-rendering-csr). From cdc41753c1586158fc52200b539793812f3c67b6 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 19 Nov 2024 07:06:31 -0500 Subject: [PATCH 5/5] Update --- aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md b/aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md index a444e6fa2d2c..0e1002e9d0bf 100644 --- a/aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md +++ b/aspnetcore/grpc/why-migrate-wcf-to-dotnet-grpc.md @@ -1,6 +1,6 @@ --- title: Why migrate WCF to ASP.NET Core gRPC -author: markrendle +author: jamesnk description: This article provides a summary of why ASP.NET Core gRPC is a good fit for Windows Communication Foundation (WCF) developers who want to migrate to modern architectures and platforms. monikerRange: '>= aspnetcore-3.0' ms.author: wpickett