1

Suppose we are writing the following structured log message

logger.Info("User's Password is {Password}", "1234567890");

I would like to mask a password property because it is a sensitive data. I found this issue but I think it is a very difficult way.

For example, I have found the extension that resolves a similar task for serilog. It is very simple to use. But I didn't find any useful information for Nlog.

How to achieve it with nlog library? I will appreciate any suggestions.

Alexcei Shmakov
  • 2,203
  • 3
  • 19
  • 34
  • The linked GitHub issue [1155](https://github.com/NLog/NLog/issues/1155) tells you to use a [replace layout renderer](https://github.com/NLog/NLog/wiki/Replace-Layout-Renderer) for that. Have you tried to use it, which issue did you get? – Pavel Anikhouski Apr 20 '20 at 19:24
  • @PavelAnukhouski, I don't exactly understand how to use the replace layout render for structured log message. My hole can have any value. And what is a search string value I need to specify in this case. – Alexcei Shmakov Apr 20 '20 at 19:40
  • Right now I think NLog only has JsonLayout-property `ExcludeProperties` and `${all-event-properties}` has `Exclude`. Cannot think of any current options that allows one to control what properties are included in the formatted message.PullRequests are always welcome. – Rolf Kristensen Apr 20 '20 at 19:53
  • You could do this with RegisterObjectTransformation? See https://nlog-project.org/2020/03/28/nlog-4-7-has-been-released.html. Reflection needed, but could be transformed to a plugin. – Julian Apr 20 '20 at 20:38
  • @Julian, don't know yet, thanks for the tip – Alexcei Shmakov Apr 21 '20 at 06:52

2 Answers2

3

You could use RegisterObjectTransformation, introduced in NLog 4.7.

For example:

LogManager.Setup().SetupSerialization(s => 
    s.RegisterObjectTransformation<object>(o =>
    {
        var props = o.GetType().GetProperties();
        var propsDict = props.ToDictionary(p => p.Name, p => p.GetValue(o));

        propsDict.Remove("password");

        return propsDict;
   }));

Please note, in terms of performance you maybe need something like a reflection cache and smart optimizations.

JoelFan
  • 37,465
  • 35
  • 132
  • 205
Julian
  • 33,915
  • 22
  • 119
  • 174
  • It will only handle properties on objects. It will not handle properties provided directly with the message-template. – Rolf Kristensen Apr 21 '20 at 20:13
  • Yes, it works only for properties, for example `Log.Information("Logged on {@User}", new User { Username = "sudo", Password = "SuperAdmin" }); ` from the linked extension (https://github.com/evjenio/masking.serilog) – Julian Apr 21 '20 at 20:17
1

I suggest that you wrap the secret password in an object like this:

public class SecretWrapper : IFormatable
{
    private readonly string _secret;

    public SecretWrapper(string secret)
    {
        _secret  = secret;
    }

    public string GetSecret() => _secret; // Not a property to avoid basic reflection

    public override string ToString() => "******"

    public string ToString (string format, IFormatProvider formatProvider) => ToString();
}

NLog will never output the secret-value:

logger.Info("User's Password is {Password}", new SecretWrapper("1234567890"));
Rolf Kristensen
  • 17,785
  • 1
  • 51
  • 70