티스토리 뷰

masking은 민감 정보에 대해 보이지 않게 처리를 해주는것을 말한다. 개인정보와 관련된 시스템을 구축하다보면 masking은 필수로 사용이 되어야 한다. 화면단에 보이는 masking도 필요하고 출력되는 log에도 masking은 필요하다. 그중 이번에는 log에 masking을 적용하는 방법에 대해 알아보도록 하겠다. 

필자는 springboot 환경에 logback을 사용하고 있다. springboot의 application.yml 에서 logback 설정을 하여 log를 관리하고 있었다. 하지만 log를 masking 하는것은 application.yml에서는 할수 없었다. application.yml에서는 default로만 설정을 할 수밖에 없어서 커스터마이징이 필요한 log masking은 사용할 수 없었다. 따라서 예전 방식인 logback-spring.xml 파일을 다시 만들어서 이곳에서 logback 설정을 해야지 가능했다.

(application.yml 파일에서 적용할 수 있는 방법을 아시는분은 공유 부탁드립니다.)


AS-IS data & log

Map<String, String> user = new HashMap<String, String>();
user.put("user_id", "87656");
user.put("SSN", "786445563");
user.put("address", "22 Street");
user.put("city", "Chicago");
user.put("Country", "U.S.");
user.put("ip_address", "192.168.1.1");
user.put("email_id", "spring@oing.com");
JSONObject userDetails = new JSONObject(user);

log.debug("User JSON: {}", userDetails);

위와 같은 사용자 정보를 출력하는 log가 있다. 출력을 해보면 다음과 같은 로그를 확인할 수 있다. 

{
    "user_id":"87656",
    "ssn":"786445563",
    "address":"22 Street",
    "city":"Chicago",
    "Country":"U.S.",
    "ip_address":"192.168.1.1",
    "email_id":"spring@oing.com"
 }

 

모든 사항이 민감정보이긴 하지만 이중 몇개만 log 출력시 masking 처리를 해보도록 하겠다. 

 

MaskingPatternLayout.java

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;

public class MaskingPatternLayout extends PatternLayout {

    private Pattern multilinePattern;
    private List<String> maskPatterns = new ArrayList<>();

    public void addMaskPattern(String maskPattern) {
        maskPatterns.add(maskPattern);
        multilinePattern = Pattern.compile(maskPatterns.stream().collect(Collectors.joining("|")), Pattern.MULTILINE);
    }

    @Override
    public String doLayout(ILoggingEvent event) {
        return maskMessage(super.doLayout(event));
    }

    private String maskMessage(String message) {
        if (multilinePattern == null) {
            return 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, '*'));
                }
            });
        }
        return sb.toString();
    }
}

이 클래스의 역할은 특정 단어가 들어오면 이것을 지정한 정규식으로 치환을 해주는 사용자정의 PatternLayout이다. 일반적으로 masking에서 그러하듯 여기에서도 문자를 * 로 치환을 해준다. 작성을 할때는 logback의 PatternLayout을 상속받아 구현을 해줘야 한다. 구현을 하고 적당한 위치에 두도록 하자. 

 

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>[%d{HH:mm:ss.SSS}][%-5level][%logger{36}.%method:line%line] - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="mask" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
           <layout class="oing.test.MaskingPatternLayout">
               <maskPattern>\"SSN\"\s*:\s*\"(.*?)\"</maskPattern> <!-- SSN JSON pattern -->
               <maskPattern>\"address\"\s*:\s*\"(.*?)\"</maskPattern> <!-- Address JSON pattern -->
               <maskPattern>(\d+\.\d+\.\d+\.\d+)</maskPattern> <!-- Ip address IPv4 pattern -->
               <maskPattern>(\w+@\w+\.\w+)</maskPattern> <!-- Email pattern -->
               <pattern>%-5p [%d{ISO8601,UTC}] [%thread] %c: %m%n%rootException</pattern>
            </layout>
        </encoder>
    </appender>
    
    <logger name="oing" level="debug" additivity="false">
        <appender-ref ref="mask" />
    </logger>        
                    
    <root level="info">
        <appender-ref ref="console" />
    </root>
</configuration>

그리고 위에서도 언급했듯이 log를 application.yml에서 설정을 하는것이 아닌 logback-spring.xml 파일을 만들어서 위와 같은 내용을 넣어준다. <layout>에 내가 위에서 작성한 MaskingPatternLayout을 매핑시켜주고 making을 적용할 pattern들을 넣어준다. SSN과 address, IP, Email 에 대한 정보를 발견하면 * 로 치환을 할 것이다. 

여기까지 진행했다면 어플리케이션을 기동하고 잘 되는지 확인을 해보자. 

 

TO-BE log

{
    "email_id":"*******************",
    "address":"*********",
    "user_id":"87656",
    "city":"Chicago",
    "Country":"U.S.",
    "ip_address":"***********",
    "SSN":"*********"
}

위와 같이 logback-spring.xml 파일에서 정의한 항목에 맞게 masking 처리가 된것을 확인할 수 있다. 

이 뿐만 아니라 oing이라는 package 아래에 있는 모든 logging을 하는 행위에 대해서 항목이 일치하다면 masking 처리가 되는것을 확인할 수 있다. 

 

참조 : https://www.baeldung.com/logback-mask-sensitive-data

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31