Logo

dev-resources.site

for different kinds of informations.

Mask logs using logstash logback in java with regex

Published at
11/11/2024
Categories
java
architecture
logging
springboot
Author
dayanandaeswar
Author
14 person written this
dayanandaeswar
open
Mask logs using logstash logback in java with regex

In today's data-driven world, data security is most important. Logging frameworks play a crucial role in application monitoring and debugging, but they can inadvertently expose sensitive information which should not have been. Log masking is a technique that effectively obfuscates sensitive data in log messages, safeguarding confidential information.

Understanding Logback

Logback is a powerful and mostly used logging framework in Java applications. It offers flexible configuration options, including the ability to format log events as JSON objects. It is a successor to the Log4j framework, has quickly gained popularity due to its features and ease of use. It is comprised of Logger, Encoders, Layout, Appender, Encoder.

Logger: Logger is a context for log messages. Applications will interact with this class to create log messages.

Encoders: Encoders are introduced in logback 0.9.91 and responsible for transforming an event into a byte array as well as writing out that byte array into an OutputStream. Encoders introduced as Layouts are only capable to transform an event into a String which restricts their scope to non-binary output.

Layout: The layout is responsible for formatting the logging request according to the user's wishes, whereas an appender takes care of sending the formatted output to its destination.

Appenders: In logback speak, an output destination is called an appender. This places log messages in their final destinations. A Logger can have more than one Appender. Currently, appenders exist for the console, files, remote socket servers, to MySQL, PostgreSQL, Oracle and other databases, JMS, and remote UNIX Syslog daemons.

About Logstash Logback Encoder

The logstash-logback-encoder library is a valuable tool for enhancing your Spring Boot applications' logging capabilities. It provides a convenient way to format log messages in a structured JSON format, making them readily consumable by log aggregation and analysis tools like Logstash. JSON format provides a structured and machine-readable way to log information, making it ideal for advanced log analysis and security measures. Benefits of logstash

  • JSON Customization Logstash allows you to customize the JSON output to include specific fields and metadata.

  • Dynamically Fields It also allows Dynamically add fields to log events based on application context.

  • Improved Readability JSON format offers a clear and human-readable structure for log events.

  • Enhanced Search and Analysis Log aggregation tools can easily parse and query JSON logs.

  • Machine Parsing JSON logs are ideal for automated analysis and alerting systems.

Solution to mask the data in logs

Here main goal is to provide a solution to mask the data which is customizable and configurable at runtime.

Here is our simple requirement to

  1. mask password completely in log.
  2. mask phone number and login name except last 5 in log.

Step 1
Create spring boot application. This solution will work with any java based application with little customization.

Step 2
Configure all regular expressions to mask the data. Please remember regex are costly in terms of resource utilization. Make sure you are tuning your regex. Regex groups will allows us to select required substring from string.

Props configuration

Step 3
Create a class and implement MessageJsonProvider. This interface is from logstash and allows us to customize the message before printing to the appender. writeTo method in this interface will be called for every log message.

  • in start() method read all regex and prepare LogMasker which contains all MaskingRule. This method is from AbstractJsonProvider and simply marks the process started as true.

  • MaskingRule will hold regex pattern and a function. This function replaces the identified string from in the log.

@Data
public class MaskingMessagingProvider extends MessageJsonProvider {

    public static final String DEFAULT_RULES_DELIMITER = ",";
    private LogMasker logMasker;
    private String rules;

    public MaskingMessagingProvider() {
        super();
    }

    @Override
    public void start() {
        super.start();
        this.logMasker = LogMasker.create(StringUtils.tokenizeToStringArray(rules, DEFAULT_RULES_DELIMITER));
    }

    @Override
    public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException {

        if (isStarted()) {
            JsonWritingUtils.writeStringField(generator, getFieldName(), logMasker.mask(event.getFormattedMessage()));
        }
    }
}

class LogMasker {

    private MaskingRule[] masks;

    public LogMasker(MaskingRule[] masks) {
        super();
        this.masks = masks.clone();
    }

    public static LogMasker create(String[] rules) {

        return new LogMasker(Arrays.stream(rules).map(rule -> MaskingRule.create(rule)).toArray(MaskingRule[]::new));
    }

    public String mask(String input) {
        String transformed = input;
        for (MaskingRule m : masks) {
            transformed = m.mask(transformed);
        }
        return transformed;
    }
}

class MaskingRule {
    public static final int REG_EX_DEFAULT_GROUP_SELECTOR = 2;
    public static final String DEFAULT_REPLACEMENT = "*";

    private Pattern pattern;
    private UnaryOperator<String> replacement;

    public MaskingRule(Pattern maskPattern, UnaryOperator<String> replacement) {
        super();
        this.pattern = maskPattern;
        this.replacement = replacement;
    }

    public static MaskingRule create(String rule) {
        return new MaskingRule(Pattern.compile(rule), (in) -> MaskingRule.maskDataWithReplacement(in, DEFAULT_REPLACEMENT));
    }

    public String mask(String transformed) {
        Matcher matcher = pattern.matcher(transformed);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, replacement.apply(getDataToBeMasked(matcher)));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    private static String maskDataWithReplacement(String input, String replacement) {
        int repetition = !StringUtils.hasLength(input) ? 0 : input.length();
        return String.join("", Collections.nCopies(repetition, replacement));
    }

    private static String getDataToBeMasked(Matcher matcher) {
        if (matcher.groupCount() > 1) {
            return matcher.group(REG_EX_DEFAULT_GROUP_SELECTOR);
        }
        return matcher.groupCount() > 0 ? matcher.group(1) : "";
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4
Configure class in logback-spring.xml file.

<configuration>
    <springProperty scope="context" name="rules" source="app.logging.masking.rules"
                    defaultValue=""/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <provider class="com.daya.logging.logstash.MaskingMessagingProvider">
                    <rules>${rules}</rules>
                    <rulesDelimiter>${rulesDelimiter}</rulesDelimiter>
                    <ruleDelimiter>${ruleDelimiter}</ruleDelimiter>
                </provider>
                <threadName/>
                <timestamp/>
                <logLevel/>
                <loggerName/>
                <mdc/>
                <version/>
                <stackTrace/>
            </providers>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>
Enter fullscreen mode Exit fullscreen mode

Steps 5
Run the application. For simplicity, i have taken a string which is holding data and printing it at application start up.

@SpringBootApplication
@Slf4j
public class LogDataMaskingApplication {

    public static void main(String[] args) {
        SpringApplication.run(LogDataMaskingApplication.class, args);
        LogDataMaskingApplication.maskingTest();
    }

    public static void maskingTest() {
        String data = "{\"loginName\":\"maskingtest\",\"phoneNumber\":\"9898981212\",\"password\":\"Masking@123\"}";
        log.info(data);
    }

}
Enter fullscreen mode Exit fullscreen mode

Log Masked

This is very basics solution and having lot of scope for enhancement as per requirement like message digest etc...

You can find code at GitHub.

Please drop a comment if you have any question.

logging Article's
30 articles in total
Favicon
🐹 Golang Integration with Kafka and Uber ZapLog 📨
Favicon
Mastering GoFrame Logging: From Zero to Hero
Favicon
Quickly and easily filter your Amazon CloudWatch logs using Logs Insights
Favicon
Avoiding console.log in Production: Best Practices for Robust Logging
Favicon
Freeware: Java Utility Package (Version 2024.12.08) released
Favicon
Kubernetes DaemonSets: Managing System-Level Components Across Every Node
Favicon
AWS CloudWatch: Implementing Data Protection Policy for Sensitive Log Data!
Favicon
Mastering Python Logging: From Basics to Advanced Techniques
Favicon
Docker Logging Drivers: A Comprehensive Guide for Effective Log Management
Favicon
How to Contact Robinhood Support Without Logging In
Favicon
Best Practices for Effective Logging Strategies
Favicon
How EKF Simplifies Logging
Favicon
Introducing implicit contexts in LogTape 0.7.0
Favicon
Simple Python Logging - and a digression on dependencies, trust, and Copy/pasting code
Favicon
Creating a Robust Logging System in C
Favicon
Understanding Logging in Kubernetes - From Containers to Nodes
Favicon
Making Wooster Talk: A Deep Dive into Structured Logging
Favicon
Logging con Python
Favicon
Freeware: Java Utility Package (Version 2024.10.26) released
Favicon
Is your Java log utility class reporting itself as the source of your logs? Learn how to fix it!
Favicon
Golang: Importância de planejar como exibir logs em aplicações de uso intenso
Favicon
Docker Advance Part 2: Docker Logging
Favicon
System Design 10 - Distributed Logging and Monitoring: Keeping an Eye on Your System’s Every Move
Favicon
Mask logs using logstash logback in java with regex
Favicon
Observability - 5(Logging using EFK)
Favicon
Observability
Favicon
Software Devs Picked These 2 Log Formats
Favicon
Error Handling and Logging in Node.js Applications
Favicon
Logging in Python: Best Practices
Favicon
microlog 6: New feature – Log Topics

Featured ones: