JEP draft: JFR In-Process Data Redaction
| Owner | Erik Gahlin |
| Type | Feature |
| Scope | JDK |
| Status | Submitted |
| Component | hotspot / jfr |
| Effort | M |
| Duration | M |
| Reviewed by | Markus Grönlund, Vladimir Kozlov |
| Created | 2025/11/30 02:51 |
| Updated | 2026/04/02 13:38 |
| Issue | 8372760 |
Summary
Add redaction to JDK Flight Recorder (JFR) to keep sensitive data out of recordings. This feature covers command-line arguments and the values of environment variables and system properties specified at startup. To prevent sensitive data from leaking, JFR redacts it before it leaves the process.
Motivation
JFR is a low-overhead diagnostics framework integrated into the Java Virtual Machine (JVM). It captures information about the runtime and the application, and it stores it as timestamped events in a recording file that tools such as the the jfr command and JDK Mission Control can analyze later. Recordings typically include events that contain environment variables, system properties, and command-line arguments so users can see how the process was started and configured.
These events can contain sensitive data, such as passwords in system properties, tokens in environment variables, and secrets passed as command-line arguments. Today, this data appears verbatim in recordings, which creates risk when recordings are shared, archived, or attached to support cases.
For example, a user may start their application with JFR enabled like this, not realizing what ends up in the recording file dump.jfr:
$ export ACCESS_TOKEN=secret_token_value
$ java @options.txt
-XX:StartFlightRecording:filename=dump.jfr
-Xmx2G
-jar application.jar
--dbpassword secret_password
@options.txt is an argument file containing the following option:
-Djavax.net.ssl.keyStorePassword=secret_password
The jdk.InitialEnvironmentVariable event records the ACCESS_TOKEN value. The jdk.InitialSystemProperty and jdk.JVMInformation events store the value of javax.net.ssl.keyStorePassword and the argument passed to --dbpassword.
In-process redaction prevents this while preserving non-sensitive information needed for troubleshooting, such as the heap size configuration (-Xmx2G).
Description
In the scenario described in the Motivation section, sensitive information is redacted by default, and users do not need to configure anything.
However, to explain the feature, it is easiest to use an example where this is not the case and show how to use the two new sub-options in -XX:FlightRecorderOptions. These options control how JFR filters arguments and properties so that recorded events do not store sensitive content.
-
redact-argumentapplies to command-line arguments. If a filter matches an argument, that argument is redacted. -
redact-keyapplies to environment variables and system properties recorded as key-value pairs. If a filter matches a key, the associated value is redacted.
Matching is case-insensitive. Filters use glob patterns, where * and ? are wildcards. If a filter matches a key or an argument, the corresponding value or argument stored in the event is replaced with [REDACTED]. Sub-options of -XX:FlightRecorderOptions are comma-separated. Within redact-key and redact-argument, multiple filters are separated with semicolons (;) and evaluated in the order they are specified. When a filter matches a command-line argument, the matched argument is redacted and evaluation continues with the next argument.
Example
To redact any environment variable or system property named confidential, plus any command-line argument that looks like a URL containing username:password@host, use:
$ export confidential=secret
$ java
'-XX:FlightRecorderOptions:redact-key=confidential,redact-argument=https://*:*@*'
-Dconfidential=secret
-XX:StartFlightRecording:filename=dump.jfr
-jar application.jar https://john:secret@example.com/login --verbose
To verify that sensitive information has been removed, use 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]"
}
To debug filter matching, start the JVM with -Xlog:jfr+redact=debug to identify which environment variables, system properties, or command-line arguments are redacted.
Matching a following argument
Sometimes, sensitive information in command-line options is passed as a value after an option, for example, --password <sensitive>. Matching against --passwordalone would not be sufficient, and matching only against` would require the sensitive information to be known, which is not possible in a generic, predefined filter. To remedy this, multiple arguments can be matched by inserting whitespace between the arguments.
For example, the following filter redacts all arguments named --password and any argument that follows:
$ java '-XX:FlightRecorderOptions:redact-argument=--password *' ...
To match more than two arguments, add another space, for example: --password * *.
Loading filters from a file
Filters can be loaded from a file to avoid overly long command lines. This enables reuse of the same filter set across deployments and allows redaction rules to be updated without changing JVM startup options. Filters are stored with one filter per line, and any trailing whitespace at the end of a line is removed.
To reference a file on the command line, prefix the filename with @.
$ java '-XX:FlightRecorderOptions:redact-argument=@redact-arguments.txt,redact-key=@redact-keys.txt' ...
Filter files are read at startup. If a file cannot be read, the JVM fails to start and prints an error message naming the unreadable file. To log that a redaction file is loaded successfully, use -Xlog:jfr+redact=debug.
The same file may be used for both the redact-argument and redact-key options, if appropriate.
Defaults, adding to defaults, and disabling
By default, JFR applies a built-in set of filters when redact-key or redact-argument are not specified.
The default filters for redact-key are:
*api*key*
*auth*
*client*secret*
*credential*
*jaas*config*
*jwt*
*passphrase*
*passwd*
*password*
*private*key*
*pwd*
*secret*
*token*
The default filters for redact-argument are similar to those for redact-key, but the *auth* filter is omitted to avoid matching words like author. Filters are also included to match a following argument. The filter prefix -* matches command-line options that start with - or --, for example, -password or --password.
-*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*
Returning to the scenario described in the Motivation section, this means that ACCESS_TOKEN is redacted by the default filter *token*, javax.net.ssl.keyStorePassword is matched by the *password* filter, and --dbpassword secret_password is redacted via -*password *.
To add filters on top of the defaults, prefix the first filter with +. Without +, the specified filters replace the defaults. To disable redaction for an option, use none.
$ java '-XX:FlightRecorderOptions:redact-key=+confidential;secret;@secret-keys.txt,redact-argument=none' ...
Syntax
The following grammar defines the option value syntax.
option-value ::= 'none' | filters
filters ::= ['+'] filter (';' filter)*
filter ::= expression | '@' filename
glob-pattern ::= characters (including '*' and '?' wildcards), matched case-insensitively
For redact-key:
expression ::= glob-pattern
For redact-argument:
expression ::= glob-pattern (' ' glob-pattern)*
Alternatives
-
Use the existing jfr scrub command to manually remove events that contain sensitive data from recording files. This is repetitive and error-prone. Also, as part of normal operation, event data is written to a temporary directory before it becomes a recording file. If the JVM crashes, event data may be left behind in a temporary file in an unredacted state. If recording data is streamed using FlightRecorderMXBean or RemoteRecordingStream, the event data can leave the host unredacted without the user getting a chance to scrub the contents.
-
Disable events using an option such as
-XX:StartFlightRecording:jdk.InitialEnvironmentVariable#enabled=falseto prevent sensitive data from being captured. This is cumbersome to configure and may remove non-sensitive environment variables needed for troubleshooting. Users may not be aware of sensitive data being recorded, which default redaction filters can remove without advance configuration. -
Filter data using regular expressions. Implementing regex-based filtering in HotSpot would require using the C++
<regex>header, which can throw exceptions prohibited by the HotSpot Coding Style. Implementing redaction filtering in Java using classes injava.util.regexincreases startup cost due to extra class loading. It also complicates extending redaction filters to new events where an upcall from the JVM to Java is not possible. Furthermore, the expressiveness of regular expressions is typically unnecessary.
Risks and Assumptions
With defaults enabled, recordings may differ from earlier releases by showing [REDACTED] in affected fields. To preserve prior behavior, users can specify:
-XX:FlightRecorderOptions:redact-argument=none,redact-key=none