I'm using below logstash encoder in logback config of my spring boot project.
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<pattern>
<pattern>
{
"creation_timestamp": "%d{yyyy-MM-dd'T'HH:mm:ss, UTC}",
"msg": "%msg",
"log_type": "%level",
}
</pattern>
<omitEmptyFields>true</omitEmptyFields>
</pattern>
</providers>
</encoder>
I want to mask sensitive data in the json log message. I tried the below to mask the value of key "Code". But it doesn't work.
"msg": "%replace(%msg){'\"Code\":'.*'', '\"Code\":'xxx''}",
How can I mask json value within the logstash appender ?
I was able to mask sensitive data by writing a custom implementation of ValueMasker
Two components are required:
ValueMasker
jsonGeneratorDecorator
in your logback.xml
package com.mask;
import com.fasterxml.jackson.core.JsonStreamContext;
import net.logstash.logback.mask.ValueMasker;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
public class SensitiveMaskingPatternLayout implements ValueMasker {
private Pattern multilinePattern = Pattern.compile("\\\"msg\\\"\\s*:\\s*\\\"(.*?)\\\"", Pattern.MULTILINE);;
private List<String> maskPatterns = new ArrayList<>();
private String maskMessage(String message) {
StringBuilder sb = new StringBuilder(message);
Matcher matcher = multilinePattern.matcher(sb);
while (matcher.find()) {
IntStream.rangeClosed(1, matcher.groupCount()).forEach(group -> {
if (matcher.group(group) != null) {
IntStream.range(matcher.start(group), matcher.end(group)).forEach(i -> sb.setCharAt(i, 'x'));
}
});
}
return sb.toString();
}
@Override
public Object mask(JsonStreamContext context, Object value) {
if (value instanceof CharSequence) {
return maskMessage((String) value);
}
return value;
}
}
<configuration>
<appender name="mask" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<pattern>
<pattern>
{
"msg": "%msg",
"log_type": "%level"
}
</pattern>
<omitEmptyFields>true</omitEmptyFields>
</pattern>
</providers>
<logLevel>
INFO
</logLevel>
<jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
<valueMasker class="com.mask.SensitiveMaskingPatternLayout"/>
</jsonGeneratorDecorator>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="mask" />
</root>
</configuration>
Example:
package com.example;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App {
private static final Logger logger = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
Map<String, String> user = new HashMap<>();
user.put("user_id", "97588");
user.put("email_id", "random@email.com");
user.put("msg", "hello world");
JSONObject userDetails = new JSONObject(user);
logger.info("MaskingPatternExample log from {}" + userDetails);
}
}
{"msg":"MaskingPatternExample log from {}{\"email_id\":\"random@email.com\",\"msg\":\"xxxxxxxxxxx\",\"user_id\":\"97588\"}","log_type":"INFO"}
Further documentation.