首页 文章

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

提问于
浏览
7

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

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

  • Cardnumber / PAN清洁剂

  • 密码清理器

  • 私人数据清理工具

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

Option 1:

我发现这篇StackOverflow文章详细介绍了一种可能的解决方案,但它涉及到反射的使用 . 这对于性能来说是不可取的,但是操作内部存储机制似乎也很麻烦 . Editing-log4net-messages-before-they-reach-the-appenders

Option 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", "*"));
   }
}
<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>

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

Option 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));
   }
}

匹配配置(很难遵循):

<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>

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

1 回答

  • -1

    在你的问题中,你已经说过你应该去找原因并且不记录任何敏感数据 . 这可以通过使用代码评审的第四种选项来强制执行,并查看记录的数据 . 您的日志记录语句永远不应记录任何敏感数据,因为这会导致安全风险 . 如果您对项目进行更改,那么信任任何带过滤器的代码,敏感数据可能会失败 . 你的QA过程必须非常好地捕捉到这种错误(我从未见过测试人员通过所有日志) . 因此,我会选择第4项,确保您不首先记录此类信息 .

相关问题