1. Problem
Writing log when developing the application will help the developer to easy debugging and tracing. But writing a good log is not enough, because some sensitive data maybe exposed in log such as: password, account number or something like that.
2. Idea
To prevent this issue, we need to replace all sensitive words to any mask character.
The purpose of this article is help you implement the simple Sensitive data destructing policy with Serilog - one of common logger extension in .NET Core. By applying this policy you can prevent the sensitive data exposure in you log. It's enough, let's go through the code...
3. Implementation
3.1. Tech stack
- .NET Core (on this article, I use .NET 6.0)
- Serilog
3.2 Show the code
- To implement the Destructing Policy we need to implement the interface IDestructuringPolicy from namespace Serilog.Core
public class SensitiveDataDestructuringPolicy : IDestructuringPolicy
{
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
{
throw new NotImplementedException();
}
}
- Now, we need to define a mask value (such as * character) and list of sensitive keywords
var mask = "******";
var sensitiveKeywords = new List<string> {
"Password", "NewPassword",
};
Actually, we should define the list of sensitive keywords some where such as Configuration file.
- We will lookup all Log Event Properties, if we found any sensitive keyword in the log object, we will create a new Log Event Property with the mask value and add the new this property to the new list of Log Event Properties, for non sensitive keyword, we also create a new Log Event Property, but keep the original value. After that, just return the result.
Bellow is complete code.
using Microsoft.Extensions.Configuration;
using Serilog.Core;
using Serilog.Events;
using System.Reflection;
namespace Microsoft.Extensions.Logging;
public class SensitiveDataDestructuringPolicy : IDestructuringPolicy
{
private const string DEFAULT_MASK_VALUE = "******";
private const string SENSITIVE_KEYWORDS_SECTION = "Logging:SensitiveData:Keywords";
private const string MASK_VALUE = "Logging:SensitiveData:Mask";
private readonly IConfiguration _configuration;
public SensitiveDataDestructuringPolicy(IConfiguration configuration)
{
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
{
var sensitiveKeywords = _configuration.GetValue<string[]>(SENSITIVE_KEYWORDS_SECTION) ?? Array.Empty<string>();
var maskValue = _configuration.GetValue<string>(MASK_VALUE) ?? DEFAULT_MASK_VALUE;
if (!sensitiveKeywords.Any())
{
result = new StructureValue(new LogEventProperty[] { });
return false;
}
var props = value.GetType().GetTypeInfo().DeclaredProperties;
var logEventProperties = new List<LogEventProperty>();
foreach (var propertyInfo in props)
{
if (sensitiveKeywords.Any(x => x.Equals(propertyInfo.Name, StringComparison.InvariantCultureIgnoreCase)))
{
logEventProperties.Add(new LogEventProperty(propertyInfo.Name, propertyValueFactory.CreatePropertyValue(maskValue)));
}
else
{
logEventProperties.Add(new LogEventProperty(propertyInfo.Name, propertyValueFactory.CreatePropertyValue(propertyInfo.GetValue(value))));
}
}
result = new StructureValue(logEventProperties);
return true;
}
}
- Finally, add the SensitiveDataDestructuringPolicy to the loggerConfiguration as below:
# Rest of code
.UseSerilog((context, services, configuration) =>
{
var loggerConfiguration = configuration
.ReadFrom.Configuration(context.Configuration)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", context.HostingEnvironment.ApplicationName)
.Enrich.WithProperty("Environment", context.HostingEnvironment.EnvironmentName)
.MinimumLevel.Information();
loggerConfiguration.Destructure.With<SensitiveDataDestructuringPolicy>();
});
4. Concluding
By applying the SensitiveDataDestructuringPolicy, you will make the log safer, that help to prevent the sensitive data exposing while tracing log.
Notes: The sample above just a very simple example, and you can modify, optimize to fit with your project.
This article was originally published at: https://sangau.me/prevent-sensitive-data-exposure-in-log-with-serilog
Top comments (4)
Nice work, but would be nice to see a screenshot of the masked output. This is important stuff great to see engineers sharing their knowledge
Fisrt of all, thank you for your comment, the screenshot is the cover image of this article. Actually, I already applyed this to the company project before posting to here!
Totally missed that on mobile!!! Still worthwhile to repeat it. The best examples also exist in real life projects too!!!
Noted, next time I will add the demo on each articles