1. 概要

大量のデータがログに記録されるため、ログに記録する際にユーザーの機密情報をマスクすることが重要です。 GDPRが存在する新しい世界では、多くの懸念の中で、個人の機密データの記録に特別な注意を払う必要があります。

このチュートリアルでは、ログバックを使用してログ内の機密データをマスクする方法を説明します。 全体として、このアプローチは問題を解決するための実際の方法ではありません。これは、ログファイルの最後の防衛線のようなものです。

2. ログバック

Logback は、Javaコミュニティで最も広く使用されているロギングフレームワークの1つです。 これは、その前身であるLog4jの代替品です。 Log4jよりも高速な実装を提供し、古いログファイルをアーカイブする際の構成オプションと柔軟性を提供します。

機密データとは、不正アクセスから保護することを目的とした情報です。 これには、社会保障番号などの個人情報(PII)から、銀行情報、ログイン資格情報、住所、電子メールなど、あらゆるものが含まれます。

アプリケーション内でログインしている間、ユーザーに属する機密データをマスクします。

3. マスキングデータ

Webリクエストのコンテキストでユーザーの詳細をログに記録するとします。 ユーザーに関連する機密データをマスクする必要があります。 アプリケーションが、ログに記録した次の要求または応答を受信したと仮定します。

{
    "user_id":"87656",
    "ssn":"786445563",
    "address":"22 Street",
    "city":"Chicago",
    "Country":"U.S.",
    "ip_address":"192.168.1.1",
    "email_id":"[email protected]"
 }

ここでは、 ssn address ip_address email_idなどの機密データがあることがわかります。 したがって、ロギング中にこのデータをマスクする必要があります。

Logbackによって生成されたすべてのログエントリのマスキングルールを構成することにより、ログを一元的にマスキングします。 これを行うには、カスタムch.qos.logback.classic.PatternLayoutを実装する必要があります。

3.1. PatternLayout

構成の背後にある考え方は、カスタムレイアウトを使用して必要なすべてのログバックアペンダーを拡張することです。 この例では、PatternLayoutの実装としてMaskingPatternLayoutクラスを記述します。各マスクパターンは、1つのタイプの機密データに一致する正規表現を表します。

MaskingPatternLayoutクラスを作成しましょう。

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

PatternLayoutdoLayout()の実装は、構成されたパターンの1つと一致する場合、アプリケーションの各ログメッセージで一致したデータをマスクする役割を果たします。

maskPatterns list from logback.xml は、複数行のパターンを作成します。 残念ながら、Logbackエンジンはコンストラクターインジェクションをサポートしていません。 プロパティのリストとして提供される場合、addMaskPatternがすべての構成エントリに対して呼び出されます。 したがって、リストに新しい正規表現を追加するたびに、パターンをコンパイルする必要があります。

3.2. 構成

一般に、機密性の高いユーザーの詳細をマスクするために正規表現パターンを使用できます

たとえば、SSNの場合、次のような正規表現を使用できます。

\"SSN\"\s*:\s*\"(.*)\"

また、アドレスには次のものを使用できます。

\"address\"\s*:\s*\"(.*?)\" 

さらに、IPアドレスデータパターン(192.169.0.1)の場合、正規表現を使用できます。

(\d+\.\d+\.\d+\.\d+)

最後に、電子メールの場合、次のように書くことができます。

(\w+@\w+\.\w+)

次に、logback.xmlファイル内のmaskPatternタグにこれらの正規表現パターンを追加します。

<configuration>
    <appender name="mask" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
           <layout class="com.baeldung.logback.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>
</ configuration>

3.3. 実行

次に、上記の例のJSONを作成し、 logger.info()を使用して詳細をログに記録します。

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", "[email protected]");
JSONObject userDetails = new JSONObject(user);

logger.info("User JSON: {}", userDetails);

これを実行すると、次の出力が表示されます。

INFO  [2021-06-01 16:04:12,059] [main] com.baeldung.logback.MaskingPatternLayoutExample: User JSON: 
{"email_id":"*******************","address":"*********","user_id":"87656","city":"Chicago","Country":"U.S.", "ip_address":"***********","SSN":"*********"}

ここで、ロガーのユーザーJSONがマスクされていることがわかります。

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

このアプローチでは、logback.xmlmaskPatternで正規表現を定義したログファイル内のデータのみをマスクできます。

4. 結論

このチュートリアルでは、 PatternLayout 機能を使用して、Logbackを使用してアプリケーションログ内の機密データをマスクする方法について説明しました。 また、特定のデータをマスクするためにlogback.xmlに正規表現パターンを追加する方法も確認しました。

いつものように、コードスニペットはGitHub利用できます。