Raghvendra
Raghvendra

Reputation: 115

How masking of sensitive data is achieved using slf4j framework?

I wanted to mask the sensitive data like username/password using an slf4j framework. Immediate help is appreciated. Thanks in advance.

Upvotes: 4

Views: 23313

Answers (4)

Alexandr Kovalenko
Alexandr Kovalenko

Reputation: 1051

Try this one.

  1. First of all, we should create a class for handling our logs (each row)

    public class PatternMaskingLayout extends PatternLayout {
    
        private Pattern multilinePattern;
        private List<String> maskPatterns = new ArrayList<>();
    
        public void addMaskPattern(String maskPattern) { // invoked for every single entry in the xml
            maskPatterns.add(maskPattern);
            multilinePattern = Pattern.compile(
                String.join("|", maskPatterns), // build pattern using logical OR
                Pattern.MULTILINE);
        }
    
        @Override
        public String doLayout(ILoggingEvent event) {
            return maskMessage(super.doLayout(event)); // calling superclass method is required
        }
    
        private String maskMessage(String message) {
            if (multilinePattern == null) {
                return message;
            }
            StringBuilder sb = new StringBuilder(message);
            Matcher matcher = multilinePattern.matcher(sb);
            while (matcher.find()) {
                if (matcher.group().contains("creditCard")) {
                    maskCreditCard(sb, matcher);
                } else if (matcher.group().contains("email")) {
                    // your logic for this case
                }
            }
            return sb.toString();
        }
    
        private void maskCreditCard(StringBuilder sb, Matcher matcher) {
            //here is our main logic for masking sensitive data
            String targetExpression = matcher.group();
            String[] split = targetExpression.split("=");
            String pan = split[1];
            String maskedPan = Utils.getMaskedPan(pan);
            int start = matcher.start() + split[0].length() + 1;
            int end = matcher.end();
            sb.replace(start, end, maskedPan);
        }
    }
    
  2. The second step is we should create appender for logback into logback.xml

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="com.bpcbt.micro.utils.PatternMaskingLayout">
                <maskPattern>creditCard=\d+</maskPattern> <!-- SourcePan pattern -->
                <pattern>%d{dd/MM/yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%ex</pattern>-->
            </layout>
        </encoder>
    </appender>
    
  3. Now we can use logger into our code

    log.info("card context set for creditCard={}", creditCard);
    
  4. List item

As a result, we will see

one row from logs

card context set for creditCard=11111******111

without these options, our logs would be like this row

card context set for creditCard=1111111111111

Upvotes: 5

Igor
Igor

Reputation: 34011

Assuming you're using Java/Groovy. In your log4j2.xml, add something like:

<PatternLayout pattern="%mm"/>

then take that pattern and create a converter for it:

@Plugin(name = 'maskLog', category = 'Converter')
@ConverterKeys(['mm'])
class MaskLogConverter extends LogEventPatternConverter {
    
    private static final String NAME = 'mm'
    
    private MaskLogConverter(String[] options) {
        super(NAME, NAME)
    }
    
    static LogMaskingConverter newInstance(final String[] options) {
        return new LogMaskingConverter(options)
    }
    
    @Override
    void format(LogEvent event, StringBuilder outputMessage) {
        String message = event.message//.formattedMessage
        
        // Do your masking logic here
        
        outputMessage.append(message)
    }
}

Inside that class you can mask, transform, parse, etc. accordingly.

Upvotes: 3

Danil Perestoronin
Danil Perestoronin

Reputation: 1153

Maybe this library will helpfull: owasp-security-logging

It related to OWASP Security Logging Project and provide related features:

LOGGER.info("userid={}", userid);  
LOGGER.info(SecurityMarkers.CONFIDENTIAL, "password={}", password);

The intent is to produce the following output in the log:

2014-12-16 13:54:48,860 [main] INFO - userid=joebob
2014-12-16 13:54:48,860 [main] [CONFIDENTIAL] INFO - password=***********

More you can find in Wiki

Upvotes: 4

Bob Dalgleish
Bob Dalgleish

Reputation: 8227

The framework itself won't do the masking, nor should you expect it to. It's a very bad practice to pass confidential information to a reporting system. In your log.info() call, make sure to substitute the password with asterisks. There is no point in masking the username because you might as well not log anything.

log.info("Successful login: {0} ********", username);

Upvotes: -9

Related Questions