2015-05-08 31 views
7

Patrzę na użycie log4net jako mojej struktury logowania dla nowego projektu rozpoczynającego się wkrótce. Jedną z kwestii, które napotkałem podczas prototypowania, na które nie mogę znaleźć ostatecznej odpowiedzi, jest sposób czyszczenia lub maskowania treści wiadomości w konfigurowalny i uporządkowany sposób.Konfigurowalne maskowanie danych wrażliwych za pomocą log4net

Hipotetycznie powiedzmy, że chcę wprowadzić kilka środków czyszczących, ale chcę także przestrzegać zasady jednej odpowiedzialności. Niektóre czystsze przykładów:

  • CardNumber/PAN czystsze
  • czystsze Hasło
  • czystsze prywatne dane

wiem, że nigdy nie powinno być zalogowaniu tego rodzaju informacji w formie tekstowej, a kod wykonujący dzienniki nigdy świadomie tego nie robią. Chcę mieć jednak ostatni poziom ochrony, na wypadek gdyby dane uległy zniekształceniu, a wrażliwe dane w jakiś sposób wpadły gdzieś, czego nie powinno; logi są najgorszym scenariuszem.

Wariant 1:

Znalazłem ten artykuł StackOverflow którego szczegóły możliwe rozwiązanie jednak wymaga użycia refleksji. Nie jest to pożądane ze względu na wydajność, ale wydaje się również, że manipulowanie mechanizmami pamięci wewnętrznej wydaje się być hackowate. Editing-log4net-messages-before-they-reach-the-appenders

Opcja 2:

Sugerowana odpowiedź na to samo pytanie sugeruje zastosowanie PatternLayoutConverter. Jest to dobre rozwiązanie dla pojedynczej operacji czystsze, ale nie jesteś w stanie korzystać z wielu operacji, takich jak poniżej:

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> 

W przypadku nazywania kolizji, jak wykazano powyżej, konwerter ładowane ostatni będzie jeden który działa. Korzystanie z powyższego przykładu oznacza, że ​​hasła zostaną wyczyszczone, ale nie numery kart.

Wariant 3:

Trzecia opcja, która Próbowałem jest wykorzystanie przykuty przypadkach ForwarderAppender ale ta szybko komplikuje konfigurację i nie uważam, że jest to idealne rozwiązanie. Ponieważ klasa LoggingEvent ma niezmienną właściwość RenderedMessage nie jesteśmy w stanie zmienić go bez tworzenia nowej instancji klasy LoggingEvent i przepuszczenie go przez jak pokazano poniżej:

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

konfiguracja Matching (bardzo trudne do naśladowania):

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

Czy ktoś ma inną sugestię, w jaki sposób można to wdrożyć, w przypadku gdy teoretycznie liczba urządzeń czyszczących może zostać skonfigurowana kosztem wydajności?

Odpowiedz

-1

W pytaniu już mówisz, że powinieneś pójść do sprawy i nie logować żadnych poufnych danych. Można to wymusić przez czwartą opcję użycia recenzji kodu i spojrzeć na rejestrowane dane. Twoje wyciągi z rejestrowania nie powinny nigdy rejestrować żadnych poufnych danych, ponieważ powodują zagrożenie bezpieczeństwa.Ufając każdemu kodowi za pomocą filtrów, wrażliwe dane prawdopodobnie zawiodą, jeśli wprowadzisz zmiany do projektu. Twój proces zapewniania jakości musi być naprawdę dobry, aby wykryć tego rodzaju błędy (nigdy nie widziałem, aby tester przeglądał wszystkie dzienniki). Tak więc wybrałbym opcję 4, która zapewnia, że ​​nie rejestrujesz tego rodzaju informacji w pierwszej kolejności.

+0

Całkowicie zgadzam się z Tobą, że należy podjąć wszelkie środki ostrożności, aby upewnić się, że tego rodzaju dane nie są w ogóle rejestrowane. Zajmujemy się dużymi ilościami danych od naszych klientów, które następnie poddajemy dość złożonym mechanizmom reguł. Jeśli dziennik debugowania stwierdza na przykład, że numer konta xyz nie powiódł się z powodu reguły abc, ale z jakiegoś powodu wysłano nam poufne dane w polu numeru konta, chcemy to wykryć. Posiadanie logiki wykrywania w każdym punkcie, w którym można utworzyć dziennik diagnostyczny, spowodowałoby masowe powielanie kodu. Wolałbym punkt szczególny, stąd moje pytanie. –

+0

Świetny punkt na recenzje kodu. Mamy politykę ścisłego sprawdzania kodu, w której każdy fragment kodu, który zakończy się produkcją, musi zostać przejrzany przez co najmniej dwóch innych programistów. Niestety, nawet z trzema zestawami oczu na bloku kodu, rzeczy mogą zostać pominięte i chcę się upewnić, że mamy wszystkie siły obronne, które możemy zgromadzić. –