|
| 1 | +--- |
| 2 | +title: Data redaction in .NET |
| 3 | +description: Learn how to use .NET data redaction libraries to protect your application's sensitive data. |
| 4 | +ms.date: 03/21/2025 |
| 5 | +--- |
| 6 | + |
| 7 | +# Data redaction in .NET |
| 8 | + |
| 9 | +Redaction helps you sanitize or mask sensitive information in logs, error messages, or other outputs. This keeps you compliant with privacy rules and protects sensitive data. It's useful in apps that handle personal data, financial information, or other confidential data points. |
| 10 | + |
| 11 | +## Install redaction package |
| 12 | + |
| 13 | +To get started, install the [📦 Microsoft.Extensions.Compliance.Redaction](https://www.nuget.org/packages/Microsoft.Extensions.Compliance.Redaction) NuGet package: |
| 14 | + |
| 15 | +### [.NET CLI](#tab/dotnet-cli) |
| 16 | + |
| 17 | +```dotnetcli |
| 18 | +dotnet add package Microsoft.Extensions.Compliance.Redaction |
| 19 | +``` |
| 20 | + |
| 21 | +### [PackageReference](#tab/package-reference) |
| 22 | + |
| 23 | +```xml |
| 24 | +<ItemGroup> |
| 25 | + <PackageReference Include="Microsoft.Extensions.Compliance.Redaction" |
| 26 | + Version="*" /> |
| 27 | +</ItemGroup> |
| 28 | +``` |
| 29 | + |
| 30 | +--- |
| 31 | + |
| 32 | +## Available redactors |
| 33 | + |
| 34 | +Redactors are responsible for the act of redacting sensitive data. They redact, replace, or mask sensitive information. Consider the following available redactors provided by the library: |
| 35 | + |
| 36 | +- The <xref:Microsoft.Extensions.Compliance.Redaction.ErasingRedactor> replaces any input with an empty string. |
| 37 | +- The <xref:Microsoft.Extensions.Compliance.Redaction.HmacRedactor> uses `HMACSHA256` to encode data being redacted. |
| 38 | + |
| 39 | +## Usage example |
| 40 | + |
| 41 | +To use the built-in redactors, you have to register the required services. Register the services using one of the available `AddRedaction` methods as described in the following list: |
| 42 | + |
| 43 | +- <xref:Microsoft.Extensions.DependencyInjection.RedactionServiceCollectionExtensions.AddRedaction(Microsoft.Extensions.DependencyInjection.IServiceCollection)>: Registers an implementation of <xref:Microsoft.Extensions.Compliance.Redaction.IRedactorProvider> in the <xref:Microsoft.Extensions.DependencyInjection.IServiceCollection>. |
| 44 | +- <xref:Microsoft.Extensions.DependencyInjection.RedactionServiceCollectionExtensions.AddRedaction(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{Microsoft.Extensions.Compliance.Redaction.IRedactionBuilder})>: Registers an implementation of <xref:Microsoft.Extensions.Compliance.Redaction.IRedactorProvider> in the <xref:Microsoft.Extensions.DependencyInjection.IServiceCollection> and configures available redactors with the given `configure` delegate. |
| 45 | + |
| 46 | +### Configure a redactor |
| 47 | + |
| 48 | +Fetch redactors at runtime using an <xref:Microsoft.Extensions.Compliance.Redaction.IRedactorProvider>. You can implement your own provider and register it inside the `AddRedaction` call, or use the default provider. Configure redactors using these <xref:Microsoft.Extensions.Compliance.Redaction.IRedactionBuilder> methods: |
| 49 | + |
| 50 | +```csharp |
| 51 | +// This will use the default redactor, which is the ErasingRedactor |
| 52 | +var serviceCollection = new ServiceCollection(); |
| 53 | +serviceCollection.AddRedaction(); |
| 54 | + |
| 55 | +// Using the default redactor provider: |
| 56 | +serviceCollection.AddRedaction(redactionBuilder => |
| 57 | +{ |
| 58 | + // Assign a redactor to use for a set of data classifications. |
| 59 | + redactionBuilder.SetRedactor<StarRedactor>( |
| 60 | + MyTaxonomyClassifications.Private, |
| 61 | + MyTaxonomyClassifications.Personal); |
| 62 | + // Assign a fallback redactor to use when processing classified data for which no specific redactor has been registered. |
| 63 | + // The `ErasingRedactor` is the default fallback redactor. If no redactor is configured for a data classification then the data will be erased. |
| 64 | + redactionBuilder.SetFallbackRedactor<MyFallbackRedactor>(); |
| 65 | +}); |
| 66 | + |
| 67 | +// Using a custom redactor provider: |
| 68 | +builder.Services.AddSingleton<IRedactorProvider, StarRedactorProvider>(); |
| 69 | +``` |
| 70 | + |
| 71 | +Given this data classification in your code: |
| 72 | + |
| 73 | +```csharp |
| 74 | +public static class MyTaxonomyClassifications |
| 75 | +{ |
| 76 | + public static string Name => "MyTaxonomy"; |
| 77 | + |
| 78 | + public static DataClassification Private => new(Name, nameof(Private)); |
| 79 | + public static DataClassification Public => new(Name, nameof(Public)); |
| 80 | + public static DataClassification Personal => new(Name, nameof(Personal)); |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +### Configure the HMAC redactor |
| 85 | + |
| 86 | +Configure the HMAC redactor using these <xref:Microsoft.Extensions.Compliance.Redaction.IRedactionBuilder> methods: |
| 87 | + |
| 88 | +```csharp |
| 89 | +var serviceCollection = new ServiceCollection(); |
| 90 | +serviceCollection.AddRedaction(builder => |
| 91 | +{ |
| 92 | + builder.SetHmacRedactor( |
| 93 | + options => |
| 94 | + { |
| 95 | + options.KeyId = 1234567890; |
| 96 | + options.Key = Convert.ToBase64String("1234567890abcdefghijklmnopqrstuvwxyz"); |
| 97 | + }, |
| 98 | + |
| 99 | + // Any data tagged with Personal or Private attributes will be redacted by the Hmac redactor. |
| 100 | + MyTaxonomyClassifications.Personal, MyTaxonomyClassifications.Private, |
| 101 | + |
| 102 | + // "DataClassificationSet" lets you compose multiple data classifications: |
| 103 | + // For example, here the Hmac redactor will be used for data tagged |
| 104 | + // with BOTH Personal and Private (but not one without the other). |
| 105 | + new DataClassificationSet(MyTaxonomyClassifications.Personal, |
| 106 | + MyTaxonomyClassifications.Private)); |
| 107 | +}); |
| 108 | +``` |
| 109 | + |
| 110 | +Alternatively, configure it this way: |
| 111 | + |
| 112 | +```csharp |
| 113 | +var serviceCollection = new ServiceCollection(); |
| 114 | +serviceCollection.AddRedaction(builder => |
| 115 | +{ |
| 116 | + builder.SetHmacRedactor( |
| 117 | + Configuration.GetSection("HmacRedactorOptions"), MyTaxonomyClassifications.Personal); |
| 118 | +}); |
| 119 | +``` |
| 120 | + |
| 121 | +Include this section in your JSON config file: |
| 122 | + |
| 123 | +```json |
| 124 | +{ |
| 125 | + "HmacRedactorOptions": { |
| 126 | + "KeyId": 1234567890, |
| 127 | + "Key": "1234567890abcdefghijklmnopqrstuvwxyz" |
| 128 | + } |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +- The <xref:Microsoft.Extensions.Compliance.Redaction.HmacRedactorOptions> requires its <xref:Microsoft.Extensions.Compliance.Redaction.HmacRedactorOptions.Key?displayProperty=nameWithType> and <xref:Microsoft.Extensions.Compliance.Redaction.HmacRedactorOptions.KeyId?displayProperty=nameWithType> properties to be set. |
| 133 | +- The `Key` should be in base 64 format and at least 44 characters long. Use a distinct key for each major deployment of a service. Keep the key material secret and rotate it regularly. |
| 134 | +- The `KeyId` is appended to each redacted value to identify the key used to hash the data. |
| 135 | +- Different key IDs mean the values are unrelated and can't be used for correlation. |
| 136 | + |
| 137 | +> [!NOTE] |
| 138 | +> The <xref:Microsoft.Extensions.Compliance.Redaction.HmacRedactor> is still experimental, so the preceding methods will cause the `EXTEXP0002` warningm indicating it's not yet stable. |
| 139 | +> To use it, add `<NoWarn>$(NoWarn);EXTEXP0002</NoWarn>` to your project file or add `#pragma warning disable EXTEXP0002` around the calls to `SetHmacRedactor`. |
| 140 | +
|
| 141 | +### Configure a custom redactor |
| 142 | + |
| 143 | +To create a custom redactor, define a subclass that inherits from <xref:Microsoft.Extensions.Compliance.Redaction.Redactor>: |
| 144 | + |
| 145 | +```csharp |
| 146 | +public sealed class StarRedactor : Redactor |
| 147 | + |
| 148 | +public class StarRedactor : Redactor |
| 149 | +{ |
| 150 | + private const string Stars = "****"; |
| 151 | + |
| 152 | + public override int GetRedactedLength(ReadOnlySpan<char> input) => Stars.Length; |
| 153 | + |
| 154 | + public override int Redact(ReadOnlySpan<char> source, Span<char> destination) |
| 155 | + { |
| 156 | + Stars.CopyTo(destination); |
| 157 | + |
| 158 | + return Stars.Length; |
| 159 | + } |
| 160 | +} |
| 161 | +``` |
| 162 | + |
| 163 | +### Create a custom redactor provider |
| 164 | + |
| 165 | +The <xref:Microsoft.Extensions.Compliance.Redaction.IRedactorProvider> interface provides instances of redactors based on data classification. To create a custom redactor provider, inherit from <xref:Microsoft.Extensions.Compliance.Redaction.IRedactorProvider> as shown in the following example: |
| 166 | + |
| 167 | +```csharp |
| 168 | +using Microsoft.Extensions.Compliance.Classification; |
| 169 | +using Microsoft.Extensions.Compliance.Redaction; |
| 170 | + |
| 171 | +public sealed class StarRedactorProvider : IRedactorProvider |
| 172 | +{ |
| 173 | + private static readonly StarRedactor _starRedactor = new(); |
| 174 | + |
| 175 | + public static StarRedactorProvider Instance { get; } = new(); |
| 176 | + |
| 177 | + public Redactor GetRedactor(DataClassificationSet classifications) => _starRedactor; |
| 178 | +} |
| 179 | +``` |
0 commit comments