diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index 278cdcc65a43..06051bcda53f 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -580,7 +580,7 @@ An element with an `id` of `components-seconds-to-next-attempt` displays the num ``` -The Blazor Web App project template includes a `ReconnectModal` component (`Components/Layout/ReconnectModal.razor`) with collocated stylesheet and JavaScript files (`ReconnectModal.razor.css`, `ReconnectModal.razor.js`) that can be customized as needed. These files can be examined in the ASP.NET Core reference source or by inspecting an app created from the Blazor Web App project template. The component is added to the project when the project is created in Visual Studio with **Interactive render mode** set to **Server** or **Auto** or created with the .NET CLI with the option `--interactivity server` (default) or `--interactivity auto`. +The Blazor Web App project template includes a `ReconnectModal` component (`Layout/ReconnectModal.razor`) with collocated stylesheet and JavaScript files (`ReconnectModal.razor.css`, `ReconnectModal.razor.js`) that can be customized as needed. These files can be examined in the ASP.NET Core reference source or by inspecting an app created from the Blazor Web App project template. The component is added to the project when the project is created in Visual Studio with **Interactive render mode** set to **Server** or **Auto** or created with the .NET CLI with the option `--interactivity server` (default) or `--interactivity auto`. * [`ReconnectModal` component](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor) * [Stylesheet file](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.css) diff --git a/aspnetcore/fundamentals/http-logging/index.md b/aspnetcore/fundamentals/http-logging/index.md index afdd36e58444..ffc3dd186214 100644 --- a/aspnetcore/fundamentals/http-logging/index.md +++ b/aspnetcore/fundamentals/http-logging/index.md @@ -33,6 +33,7 @@ HTTP logging ***can reduce the performance of an app***, especially when logging > [!WARNING] > HTTP logging can potentially log personally identifiable information (PII). Consider the risk and avoid logging sensitive information. +> For more information about redaction, check [redacting sensitive data](#redacting-sensitive-data) ## Enable HTTP logging @@ -205,3 +206,101 @@ The following list shows the order of precedence for logging configuration: :::moniker-end [!INCLUDE[](~/fundamentals/http-logging/includes/index-6-7.md)] + +:::moniker range=">= aspnetcore-10.0" + +## Redacting sensitive data + +Http logging with redaction can be enabled by calling `AddHttpLoggingRedaction`: + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet7&highlight=9)] + +For more information about .NET's data redaction library, see [Data redaction in .NET](/dotnet/core/extensions/data-redaction). + +## Logging redaction options + +To configure options for logging with redaction, call `AddHttpLoggingRedaction` in `Program.cs`, using the lambda to configure `LoggingRedactionOptions`: + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/MyTaxonomyClassifications.cs)] +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet_redactionOptions&highlight=6)] + +With the previous redaction configuration, the output is similar to the following: + +```output +info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[9] + Request and Response: + server.address: localhost:61361 + Path: / + http.request.header.accept: + Protocol: HTTP/2 + Method: GET + Scheme: https + http.response.header.content-type: + StatusCode: 200 + Duration: 8.4684 +info: Microsoft.AspNetCore.Hosting.Diagnostics[2] + Request finished HTTP/2 GET https://localhost:61361/ - 200 - text/plain;+charset=utf-8 105.5334ms +``` + +***Note:*** Request path `/home` isn't logged because it is included in `ExcludePathStartsWith` property. `http.request.header.accept` and `http.response.header.content-type` were redacted by `Redaction.ErasingRedactor`. + +### RequestPathLoggingMode + + +`RequestPathLoggingMode` determines how the request path is logged, whether `Formatted` or `Structured`. + + +* `Formatted` logs the request path without parameters. + +* `Structured` logs the request path with parameters included. + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet_redactionOptions&highlight=9)] + +### RequestPathParameterRedactionMode + + +`RequestPathParameterRedactionMode` specifies how route parameters in the request path should be redacted, whether `Strict` or `None`. + + +* `Strict`: request route parameters are considered as sensitive and are redacted by default. + +* `None`: request route parameters are considered as non-sensitive and logged as-is by default. + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet_redactionOptions&highlight=8)] + +### RequestHeadersDataClasses + + +`RequestHeadersDataClasses` maps request headers to their data classification, which determines how they are redacted: + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet_redactionOptions&highlight=10)] + +### ResponseHeadersDataClasses + + +`ResponseHeadersDataClasses`, similar to `RequestHeadersDataClasses`, but for response headers: + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet_redactionOptions&highlight=11)] + +### RouteParameterDataClasses + + +`RouteParameterDataClasses` maps route parameters to their data classification: + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet_redactionOptions&highlight=12,13,14,15)] + +### ExcludePathStartsWith + + +`ExcludePathStartsWith` specifies paths that should be excluded from logging entirely: + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet_redactionOptions&highlight=16,17)] + +### IncludeUnmatchedRoutes + + +`IncludeUnmatchedRoutes` allows reporting unmatched routes. If set to `true`, logs whole path of routes not identified by [Routing](xref:fundamentals/routing) instead of logging `Unknown` value for path attribute: + +[!code-csharp[](~/fundamentals/http-logging/samples/8.x/Program.cs?name=snippet_redactionOptions&highlight=18)] + +:::moniker-end diff --git a/aspnetcore/fundamentals/http-logging/samples/8.x/HttpLoggingSample.csproj b/aspnetcore/fundamentals/http-logging/samples/8.x/HttpLoggingSample.csproj index 1b28a01c81c7..4b4d4398fd37 100644 --- a/aspnetcore/fundamentals/http-logging/samples/8.x/HttpLoggingSample.csproj +++ b/aspnetcore/fundamentals/http-logging/samples/8.x/HttpLoggingSample.csproj @@ -6,4 +6,9 @@ enable + + + + + diff --git a/aspnetcore/fundamentals/http-logging/samples/8.x/MyTaxonomyClassifications.cs b/aspnetcore/fundamentals/http-logging/samples/8.x/MyTaxonomyClassifications.cs new file mode 100644 index 000000000000..4b6c8fc859da --- /dev/null +++ b/aspnetcore/fundamentals/http-logging/samples/8.x/MyTaxonomyClassifications.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Compliance.Classification; + +namespace HttpLoggingSample +{ + public static class MyTaxonomyClassifications + { + public static string Name => "MyTaxonomy"; + + public static DataClassification Private => new(Name, nameof(Private)); + public static DataClassification Public => new(Name, nameof(Public)); + public static DataClassification Personal => new(Name, nameof(Personal)); + } +} diff --git a/aspnetcore/fundamentals/http-logging/samples/8.x/Program.cs b/aspnetcore/fundamentals/http-logging/samples/8.x/Program.cs index db99bbd3aff0..c157fcb07bdb 100644 --- a/aspnetcore/fundamentals/http-logging/samples/8.x/Program.cs +++ b/aspnetcore/fundamentals/http-logging/samples/8.x/Program.cs @@ -1,4 +1,4 @@ -#define THIRD // FIRST SECOND THIRD +#define THIRD // FIRST SECOND THIRD FORTH #if NEVER #elif FIRST // @@ -26,7 +26,7 @@ app.UseStaticFiles(); -app.UseHttpLogging(); +app.UseHttpLogging(); app.Use(async (context, next) => { @@ -60,11 +60,54 @@ app.Run(); // +#elif FORTH +using Microsoft.Extensions.Http.Diagnostics; +using Microsoft.AspNetCore.Diagnostics.Logging; +using Microsoft.Extensions.Compliance.Classification; +using HttpLoggingSample; +using Microsoft.Net.Http.Headers; + +// +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddHttpLogging(o => { }); +builder.Services.AddRedaction(); + +builder.Services.AddHttpLoggingRedaction(op => +{ + op.RequestPathParameterRedactionMode = HttpRouteParameterRedactionMode.None; + op.RequestPathLoggingMode = IncomingPathLoggingMode.Formatted; + op.RequestHeadersDataClasses.Add(HeaderNames.Accept, MyTaxonomyClassifications.Public); + op.ResponseHeadersDataClasses.Add(HeaderNames.ContentType, MyTaxonomyClassifications.Private); + op.RouteParameterDataClasses = new Dictionary + { + { "one", MyTaxonomyClassifications.Personal }, + }; + // Add the paths that should be filtered, with a leading '/'. + op.ExcludePathStartsWith.Add("/home"); + op.IncludeUnmatchedRoutes = true; +}); + +var app = builder.Build(); + +app.UseHttpLogging(); + +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error"); +} +app.UseStaticFiles(); + +app.MapGet("/", () => "Logged!"); +app.MapGet("/home", () => "Not logged!"); + +app.Run(); +// #elif THIRD // using HttpLoggingSample; using Microsoft.AspNetCore.HttpLogging; - +// // var builder = WebApplication.CreateBuilder(args); @@ -74,6 +117,9 @@ }); builder.Services.AddHttpLoggingInterceptor(); // +builder.Services.AddRedaction(); +builder.Services.AddHttpLoggingRedaction(op => { }); +// var app = builder.Build(); if (!app.Environment.IsDevelopment()) @@ -83,7 +129,7 @@ app.UseStaticFiles(); -app.UseHttpLogging(); +app.UseHttpLogging(); app.Use(async (context, next) => {