可通过log4net进行可配置的敏感数据屏蔽

Jus*_*ook 8 .net c# log4net replace

我正在寻找使用log4net作为我刚刚开始的新项目的首选日志框架.我在原型设计过程中遇到的一个问题是我无法找到明确的答案,那就是如何以可配置和整洁的方式清理或屏蔽消息内容.

假设我想要让几个清洁工采取行动,但我也想遵循单一责任原则.一些清洁的例子:

  • Cardnumber/PAN清洁剂
  • 密码清理
  • 私人数据清理

我知道你永远不应该用纯文本记录这种信息,执行日志的代码永远不会故意这样做.我希望得到最后一级保护,但是如果数据变得格式不正确并且敏感数据以某种方式滑入某个地方则不应该; 日志是最糟糕的情况.

选项1:

我发现这篇StackOverflow文章详细介绍了一种可能的解决方案,但它涉及到反射的使用.这对于性能来说是不可取的,但操作内部存储机制似乎也很麻烦. 编辑-log4net的的消息先于他们到达的最追加程序

选项2:

针对同一问题的建议答案建议使用PatternLayoutConverter.这适用于单个清理器操作,但您无法使用多个操作,如下所示:

public class CardNumberCleanerLayoutConverter : PatternLayoutConverter
{
   protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
   {
      string message = loggingEvent.RenderedMessage;

      // TODO: Replace with real card number detection and masking.
      writer.Write(message.Replace("9", "*"));
   }
}
Run Code Online (Sandbox Code Playgroud)
<layout type="log4net.Layout.PatternLayout">
   <converter>
      <name value="cleanedMessage" />
      <type value="Log4NetPrototype.CardNumberCleanerLayoutConverter, Log4NetPrototype" />
   </converter>
   <converter>
      <name value="cleanedMessage" />
      <type value="Log4NetPrototype.PasswordCleanerLayoutConverter, Log4NetPrototype" />
   </converter>
   <conversionPattern value="%cleanedMessage" />
</layout>
Run Code Online (Sandbox Code Playgroud)

在如上所述的命名冲突的情况下,最后加载的转换器将是被动作的转换器.使用上面的示例,这意味着将清除密码,但不会清除卡号.

选项3:

我尝试过的第三个选项是使用链接的ForwarderAppender实例,但这很快使配置复杂化,我不认为它是一个理想的解决方案.因为LoggingEvent类具有不可变的RenderedMessage属性,所以我们无法在不创建LoggingEvent类的新实例并将其传递的情况下更改它,如下所示:

public class CardNumberCleanerForwarder : ForwardingAppender
{
   protected override void Append(LoggingEvent loggingEvent)
   {
      // TODO: Replace this with real card number detection and masking.
      string newMessage = loggingEvent.RenderedMessage.Replace("9", "*");

      // What context data are we losing by doing this?
      LoggingEventData eventData = new LoggingEventData()
      {
         Domain = loggingEvent.Domain,
         Identity = loggingEvent.Identity,
         Level = loggingEvent.Level,
         LocationInfo = loggingEvent.LocationInformation,
         LoggerName = loggingEvent.LoggerName,
         ExceptionString = loggingEvent.GetExceptionString(),
         TimeStamp = loggingEvent.TimeStamp,
         Message = newMessage,
         Properties = loggingEvent.Properties,
         ThreadName = loggingEvent.ThreadName,
         UserName = loggingEvent.UserName
      };

      base.Append(new LoggingEvent(eventData));
   }
}

public class PasswordCleanerForwarder : ForwardingAppender
{
   protected override void Append(LoggingEvent loggingEvent)
   {
      // TODO: Replace this with real password detection and masking.
      string newMessage = loggingEvent.RenderedMessage.Replace("4", "*");

      // What context data are we losing by doing this?
      LoggingEventData eventData = new LoggingEventData()
      {
         Domain = loggingEvent.Domain,
         Identity = loggingEvent.Identity,
         Level = loggingEvent.Level,
         LocationInfo = loggingEvent.LocationInformation,
         LoggerName = loggingEvent.LoggerName,
         ExceptionString = loggingEvent.GetExceptionString(),
         TimeStamp = loggingEvent.TimeStamp,
         Message = newMessage,
         Properties = loggingEvent.Properties,
         ThreadName = loggingEvent.ThreadName,
         UserName = loggingEvent.UserName
      };

      base.Append(new LoggingEvent(eventData));
   }
}
Run Code Online (Sandbox Code Playgroud)

匹配配置(非常难以遵循):

<log4net>
   <appender name="LocatedAsyncForwardingAppender" type="Log4NetPrototype.LocatedAsyncForwardingAppender, Log4NetPrototype">
      <appender-ref ref="CardNumberCleanerForwarder" />
   </appender>
   <appender name="CardNumberCleanerForwarder" type="Log4NetPrototype.CardNumberCleanerForwarder, Log4NetPrototype">
      <appender-ref ref="PasswordCleanerForwarder" />
   </appender>
   <appender name="PasswordCleanerForwarder" type="Log4NetPrototype.PasswordCleanerForwarder, Log4NetPrototype">
      <appender-ref ref="LogFileAppender" />
   </appender>
   <appender name="LogFileAppender" type="Log4NetPrototype.LogFileAppender, Log4NetPrototype">
      <layout type="log4net.Layout.PatternLayout">
         <conversionPattern value="%m" />
      </layout>
   </appender>
   <root>
      <level value="DEBUG" />
      <appender-ref ref="LocatedAsyncForwardingAppender" />
   </root>
</log4net>
Run Code Online (Sandbox Code Playgroud)

有没有人有另外的建议如何实现这一点,理论上可以以性能为代价配置n个清洁剂?

Pet*_*ter -3

在您的问题中,您已经说过您应该找出原因,而不是记录任何敏感数据。这可以通过使用代码审查的第四个选项来强制执行,并查看正在记录的数据。您的日志记录语句不应记录任何敏感数据,因为这会导致安全风险。如果您对项目进行更改,则信任任何带有过滤器的代码,敏感数据可能会失败。您的 QA 流程必须非常好才能发现此类错误(我从未见过测试人员检查所有日志)。所以我会选择选项 4,即确保您一开始就不会记录此类信息。