JDK27 JFR の機密情報マスキング - JEP 536: JFR In-Process Data Redaction

blog1.mammb.com


JFR の機密情報漏洩リスク

JFR 記録ファイルには、コマンドライン引数/環境変数/システムプロパティの値を報告するイベントが含まれており、コマンドライン引数のシークレット、環境変数のアクセストークン、システムプロパティのパスワードなどの機密データが含まれる可能性がある。

これらのデータは記録ファイルにそのまま表示されるため、機密情報が漏洩するリスクがある。

例えば、以下のようにJFRを有効にした場合、

$ export ACCESS_TOKEN=SECRET_TOKEN
$ java -XX:StartFlightRecording:filename=dump.jfr \
       -Xmx2G \
       -Djavax.net.ssl.keyStorePassword=SECRET_PASSWORD \
       -jar application.jar \
      --dbpassword ANOTHER_SECRET_PASSWORD

記録ファイル dump.jfr には、以下の3つの秘密値に対するイベントが含まれる。

  • jdk.InitialEnvironmentVariable イベントは、ACCESS_TOKEN 環境変数の値を記録する
  • jdk.InitialSystemProperty イベントは、javax.net.ssl.keyStorePassword システムプロパティの値を記録する
  • jdk.JVMInformation イベントは、アプリケーションの --dbpassword コマンドラインオプションに渡された引数を記録する


JFR In-Process Data Redaction

JDK27 から JFR は、デフォルトで多くの種類の機密情報を削除するようになった。

機密情報の削除は、redact-argumentredact-key という2つのフィルタによって定義される。

  • redact-argument : コマンドライン引数のフィルタリストを指定する(フィルタリストのいずれかが引数に一致する場合、その引数は伏せ字となる)
  • redact-key : 環境変数やシステムプロパティなどのキーと値のペアのフィルタリストを指定(フィルタリストのいずれかがキーに一致する場合、関連付けられた値が伏せ字となる)

redact-argument はデフォルトでフィルタリストとして以下が利用される

-*api*key *
-*client*secret *
-*credential *
-*jaas*config *
-*jwt *
-*passphrase *
-*passwd *
-*password *
-*private*key *
-*pwd *
-*secret *
-*token *
*api*key*
*client*secret*
*credential*
*jaas*config*
*passphrase*
*passwd*
*password*
*private*key*
*pwd*
*secret*
*token*

redact-key はデフォルトでフィルタリストとして以下が利用される

*api*key*
*auth*
*client*secret*
*credential*
*jaas*config*
*jwt*
*passphrase*
*passwd*
*password*
*private*key*
*pwd*
*secret*
*token*


フィルタリストのカスタム指定

redact-argumentredact-key を指定することでフィルタリストを明示的に指定できる。

$ export CONFIDENTIAL=SOME_SECRET
$ java -XX:FlightRecorderOptions:'redact-key=confidential,redact-argument=https://*:*@*' \
       -XX:StartFlightRecording:filename=dump.jfr \
       -Dconfidential=ANOTHER_SECRET \
       -jar application.jar https://john:YET_ANOTHER_SECRET@example.com/login --verbose

この例では、以下がすべて削除される。

  • confidential または CONFIDENTIAL という名前の環境変数やシステムプロパティ
  • ユーザー名とパスワードを含む URL のように見えるコマンドライン引数(username:password@hostなど)

  • マッチングでは大文字と小文字は区別されず、グロブパターンを使用し、* と ? はワイルドカードとなる。

  • フィルタが引数またはキーに一致すると、引数またはキーの値はイベントに [REDACTED] として記録される
  • 複数のフィルタはセミコロン (;) で区切られ、指定された順序で評価される

シンタックスは以下となる。

filter-list  ::= 'none' | filters
 filters      ::= ['+'] filter (';' filter)*
 filter       ::= expression | '@' filename
 glob-pattern ::= characters (including '*' and '?' wildcards), matched case-insensitively

redact-key:

expression ::= glob-pattern

redact-argument:

expression ::= glob-pattern (' ' glob-pattern)*


機密情報削除の確認

機密情報が削除されていることを確認するには、jfr print を使用する。

$ jfr print \
      --events InitialSystemProperty,JVMInformation,StringFlag,InitialEnvironmentVariable \
      dump.jfr

jdk.JVMInformation {
  startTime = 17:39:02.196 (2026-02-15)
  jvmVersion = "Java HotSpot(TM) 64-Bit Server VM"
  jvmArguments = "-Dconfidential=[REDACTED]
   -XX:FlightRecorderOptions:redact-key=confidential,redact-argument=[REDACTED]
   -XX:StartFlightRecording:filename=dump.jfr"
  jvmFlags = "N/A"
  javaArguments = "-jar application.jar [REDACTED] --verbose"
  jvmStartTime = 17:39:02.050 (2026-02-15)
  pid = 43671
}

jdk.InitialSystemProperty {
  startTime = 17:39:02.196 (2026-02-15)
  key = "confidential"
  value = "[REDACTED]"
}

jdk.InitialSystemProperty {
  startTime = 17:39:02.196 (2026-02-15)
  key = "sun.java.command"
  value = "-jar application.jar [REDACTED] --verbose"
}

jdk.StringFlag {
  startTime = 17:39:02.196 (2026-02-15)
  name = "FlightRecorderOptions"
  value = "redact-key=confidential,redact-argument=[REDACTED]"
  origin = "Command line"
}

jdk.InitialEnvironmentVariable {
  startTime = 17:39:02.244 (2026-02-15)
  key = "CONFIDENTIAL"
  value = "[REDACTED]"
}

redact-argument および redact-key フィルタのマッチングをデバッグするには、JVM をコマンドラインオプション -Xlog:jfr+redact=debug 付きで起動し、どのコマンドライン引数、環境変数、およびシステムプロパティが編集されるかを特定する。


フィルタ指定

複数コマンドライン引数

コマンドライン引数の中には、--password SENSITIVE のように --password に続く引数で機密情報を指定するものがある。 フィルタは、空白で区切られた一連のコマンドライン引数を照合できるため、以下のようにフィルタを指定すれば良い。

$ java -XX:FlightRecorderOptions:'redact-argument=--password *' ...

外部ファイルによるフィルタリスト指定

@ に続いてファイルパスを指定することでファイルからフィルタリストを読み込むことができる(フィルタファイルには1行に1つのフィルタを記述しする。末尾の空白は無視される)。

$ java '-XX:FlightRecorderOptions:redact-argument=@args.txt,redact-key=@keys.txt' ...

デフォルトフィルタへの追加

デフォルトのフィルタに独自のリストを追加するには、最初のフィルターの前に + を付ける。

$ java -XX:FlightRecorderOptions:'redact-key=+confidential;secret;@keys.txt' ...

無効化

すべての墨消しを無効にするには、none を指定する。

$ java -XX:FlightRecorderOptions:'redact-argument=none' ...