Exercise - Redact sensitive data in cloud-native applications
You need add some logging to the order process. You'll use the redaction features of .NET to ensure that the sensitive data isn't leaked into the logs.
In this exercise, you'll:
- Add the
Microsoft.Extensions.Compliance.Redaction
NuGet package to each project. - Add the redaction service to the dependency injection container.
- Enable redaction in the logging framework.
- Call the logging framework during the order process.
- Add a custom redaction implementation for EUII data.
- Choose which redaction implementation to use for each type of classified data.
Add the redaction service
You should still have the codespace or Visual Studio Code window open. If not, open it now.
In the TERMINAL window, enter this command:
cd /workspaces/mslearn-dotnet-cloudnative/dotnet-compliance/eShopLite/Store/
Add the
Microsoft.Extensions.Compliance.Redaction
NuGet package to the project:dotnet add package Microsoft.Extensions.Compliance.Redaction
In the EXPLORER pane, expand the dotnet-compliance/eShopLite/Store folder, then select the Program.cs file.
In the editor, add the following dependencies:
using Microsoft.Extensions.Compliance.Classification; using Microsoft.Extensions.Compliance.Redaction;
Scroll down to line 19, under the
Add redaction
comment add the redaction service to the dependency injection container:builder.Services.AddRedaction();
Enabling redaction in the logging framework
In the editor, add this code below the
AddRedaction()
line:builder.Services.AddLogging(logging => { logging.EnableRedaction(); logging.AddJsonConsole(); //Enable structure logs on the console to view the redacted data. });
The above code enables redaction in the logging framework.
Call the logging framework during the order process
In the EXPLORER pane, expand the dotnet-compliance/eShopLite/Store/Services folder, then select the ProductService.cs file.
In the editor, at the bottom of the file, add this code:
public static partial class Log { [LoggerMessage(1, LogLevel.Information, "Placed Order: {order}")] public static partial void LogOrders(this ILogger logger, [LogProperties] Order order); }
In the editor, in the
CreateOrder
task, call theLogOrders
method:public async Task<bool> CreateOrder(Order order) { try { _logger.LogOrders(order);
The above code calls the
LogOrders
method and passes it the current order information.
Test the new redacted logging
With all the above code in place, the app can use the default redaction implementation to redact the Order
information. You'll now test this.
On the TERMINAL pane at the bottom, go to the dotnet-compliance/eShopLite folder.
cd ..
Update the apps containers.
dotnet publish /p:PublishProfile=DefaultContainer
Go to the dotnet-compliance folder, and start the app with Docker:
cd .. docker compose up
Select the PORTS tab, then select the Open in Browser globe icon for the Front End (32000) port.
Select the Products link. Add some products to your shopping basket.
Select the Buy Basket button.
In the TERMINAL window, press Ctrl+F, in the search field enter "EventId":1,.
frontend-1 | {"EventId":1,"LogLevel":"Information","Category":"Store.Services.ProductService","Message":"Placed Order: DataEntities.Order","State":{"Message":"Microsoft.Extensions.Logging.ExtendedLogger\u002BModernTagJoiner","{OriginalFormat}":"Placed Order: {order}","order.Total":209.94,"order.Products":"[\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022]","order":"DataEntities.Order","order.CustomerAddress":"","order.CustomerName":"","order.Id":""}}
You should see this JSON formatted log entry. Notice that the order.Total value is in the logs, but the CustomerName and CustomerAddress values are empty strings.
By default, if you don't specify a redaction implementation, the redaction engine will use the
ErasingRedactor
implementation to ensure no sensitive data is leaked into the logs.In the TERMINAL window, press Ctrl+C to stop the app.
Add a custom redaction implementation
You'll now enhance the redaction implementation to use different redaction algorithms for different types of data. First you'll add a new custom redaction implementation that replaces the value with *****
.
In the EXPLORER pane, expand the dotnet-compliance/eShopLite/DataEntities folder, then select the Compliance.cs file.
In the editor, at the bottom of the file, add this code:
public class EShopCustomRedactor : Redactor { private const string Stars = "*****"; public override int GetRedactedLength(ReadOnlySpan<char> input) => Stars.Length; public override int Redact(ReadOnlySpan<char> source, Span<char> destination) { Stars.CopyTo(destination); return Stars.Length; } }
The above code makes an
EShopCustomRedactor
redaction method available to the redaction engine.
Choose which redaction implementation to use
In the EXPLORER pane, expand the dotnet-compliance/eShopLite/Store folder, then select the Program.cs file.
Replace
builder.Services.AddRedaction();
code to provide configuration for the redaction engine:builder.Services.AddRedaction(configure => { configure.SetRedactor<ErasingRedactor>(new DataClassificationSet(DataClassifications.EUPDataClassification)); configure.SetRedactor<EShopCustomRedactor>(new DataClassificationSet(DataClassifications.EUIIDataClassification)); });
The above code configures the redaction engine to specifically use the
ErasingRedactor
implementation for EUP data and the new customEShopCustomRedactor
implementation for EUII data.
Test the new redaction implementation
In the TERMINAL window, build and run the app:
docker-compose up --build
Select the PORTS tab, then select the Open in Browser globe icon for the Front End (32000) port.
Select the Products link. Add some products to your shopping basket.
Select the Buy Basket button.
In the TERMINAL window, press Ctrl+F, in the search field enter "EventId":1,.
frontend-1 | {"EventId":1,"LogLevel":"Information","Category":"Store.Services.ProductService","Message":"Placed Order: DataEntities.Order","State":{"Message":"Microsoft.Extensions.Logging.ExtendedLogger\u002BModernTagJoiner","{OriginalFormat}":"Placed Order: {order}","order.Total":269.88,"order.Products":"[\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022,\u0022DataEntities.Product\u0022]","order":"DataEntities.Order","order.CustomerAddress":"*****","order.CustomerName":"*****","order.Id":""}}
You should see this JSON formatted log entry. Notice that the order.Id value is still an empty string, but the CustomerName and CustomerAddress values are now
*****.
In the TERMINAL window, press Ctrl+C to stop the app.