JEP draft: JFR: In-Process Data Redaction
| Owner | Erik Gahlin |
| Type | Feature |
| Scope | JDK |
| Status | Draft |
| Component | hotspot / jfr |
| Effort | S |
| Duration | S |
| Created | 2025/11/30 02:51 |
| Updated | 2026/02/16 12:57 |
| Issue | 8372760 |
Summary
Add in process redaction to JDK Flight Recorder (JFR) to keep sensitive data out of recordings. The feature covers command line arguments and the values of environment variables and system properties specified at startup.
Goals
- Provide secure by default redaction to reduce the risk of sensitive data leaving the process.
- Allow users to opt out of redaction or provide organization specific configurations.
Non-Goals
- Redact event data beyond what is available in the
jdk.InitialSystemProperty,jdk.InitialEnvironmentVariable,jdk.JVMInformation, andjdk.StringFlagevents.
Motivation
JFR is a low overhead diagnostics framework integrated into the JVM. It captures information about the runtime and the application and can be used with tools such as jfr and JDK Mission Control. Recordings typically include environment variables, system properties, and command line arguments so users can see how the process was started and configured.
These sources can contain sensitive data. Examples include passwords and tokens in properties, secrets in environment variables, and keys passed as application arguments. Today, such data can appear verbatim in recordings. That creates risk when recordings are shared, archived, or attached to support cases. Built in redaction addresses this while keeping the surrounding context needed for diagnosis.
Description
This feature adds two sub options to -XX:FlightRecorderOptions. They control how arguments and properties are filtered so sensitive content is not stored in recorded events.
-
redact-argumentapplies to command line arguments recorded by:jdk.JVMInformation(fieldsjvmArguments,jvmFlags, andjavaArguments)jdk.StringFlag(thevaluefield when it containsFlightRecorderOptions)- the value of the
sun.java.commandproperty recorded byjdk.InitialSystemProperty
The option value is a semicolon-separated list of filters. Each filter is matched against the full field value. If it matches, the field value is replaced with
"[REDACTED]". -
redact-keyapplies to values recorded byjdk.InitialEnvironmentVariableandjdk.InitialSystemProperty.The option value is a semicolon-separated list of filters. Each filter is matched against the key. If it matches, the corresponding value is replaced with
"[REDACTED]".
Matching is case insensitive. Filters use glob patterns where * and ? are wildcards.
Example
To redact any environment variable or system property named confidential, plus any startup argument that looks like an HTTP URL containing username:password@host, use:
$ export confidential=secret
$ java '-XX:FlightRecorderOptions:redact-key=confidential,redact-argument=http://*:*@*' \
-XX:StartFlightRecording:filename=recording.jfr \
-Dconfidential=sensitive \
-jar application.jar http://john:@smith@example.com/login --verbose
To verify that sensitive information has been removed, use jfr print:
$ jfr print --events InitialSystemProperty,JVMInformation,StringFlag,InitialEnvironmentVariable \
recording.jfr
jdk.JVMInformation {
startTime = 17:39:02.196 (2026-02-15)
jvmVersion = "Java HotSpot(TM) 64-Bit Server VM (build 25.0.1+8-LTS-27, mixed mode, sharing)"
jvmArguments = "-XX:FlightRecorderOptions:redact-key=confidential,redact-argument=[REDACTED]
-XX:StartFlightRecording:filename=recording.jfr -Dconfidential=[REDACTED]"
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]"
}
Multiple filters
Multiple filters for a sub option are separated with ;. In the following example, both system properties and environment variables named confidential and sensitive are redacted.
$ java '-XX:FlightRecorderOptions:redact-key=confidential;sensitive' ...
Matching a following argument
Some secrets are passed as a value after an option. To match multiple tokens, use <DELIMITER>. The first pattern matches the option token, and the second pattern matches the following tokens.
$ java '-XX:FlightRecorderOptions:redact-argument=--password<DELIMITER>*' ...
$ java '-XX:FlightRecorderOptions:redact-argument=--password<DELIMITER>*<DELIMITER>*' ...
Loading filters from a file
Filters can be stored in a file with one filter per line. To reference a file, 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 a warning that names the unreadable file. To log that a redaction file is properly loaded, use -Xlog:jfr+redact=debug
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. To add filters on top of the defaults, prefix the first filter with +. To disable redaction for an option, use none.
$ java '-XX:FlightRecorderOptions:redact-key=+confidential;secret;@redact-keys.txt,redact-argument=none' ...
Default filters (the list may be extended over time):
redact-key defaults:
*auth*;*jaas*config*;*password*;*passwd*;
*pwd*;*passphrase*;*token*;*secret*;
*credential*;*api-key*;*api_key*;*apikey*;
*client-secret*;*client_secret*;*clientsecret*;*private-key*;
*private_key*;*privatekey*
redact-argument defaults:
(all redact-key defaults except *auth*, plus)
--*jaas*config<DELIMITER>*;--*password<DELIMITER>*;
--*passwd<DELIMITER>*;--*pwd<DELIMITER>*;
--*passphrase<DELIMITER>*;--*token<DELIMITER>*;
--*secret<DELIMITER>*;--*credential<DELIMITER>*;
--*api-key<DELIMITER>*;--*api_key<DELIMITER>*;
--*apikey<DELIMITER>*;--*client-secret<DELIMITER>*;
--*client_secret<DELIMITER>*;--*clientsecret<DELIMITER>*;
--*private-key<DELIMITER>*;--*private_key<DELIMITER>*;
--*privatekey<DELIMITER>*
Syntax
Both options accept a semicolon-separated list of filters.
option-value ::= 'none' | filters
filters ::= ['+'] filter (';' filter)*
filter ::= expression | '@' filename
glob-pattern ::= character-sequence-with '*' and '?' wildcards (case insensitive)
For redact-key:
expression ::= glob-pattern
For redact-argument:
expression ::= glob-pattern ('<DELIMITER>' glob-pattern)
Alternatives
-
Use the
jfr scrubcommand to remove events that contain sensitive data. This is repetitive and error prone. Unredacted artifacts exist until scrubbing completes. Extra copies may also be left in temporary locations if the JVM crashes. If recording data is streamed usingFlightRecorderMXBeanorjdk.jfr.consumer.RemoteRecordingStream, the events can also leave the host unredacted. -
Disable events using an option such as
-XX:StartFlightRecording:jdk.InitialEnvironmentVariable#enabled=falseto prevent sensitive data from being captured. This is cumbersome and it can remove diagnostic context needed for troubleshooting. Built in redaction requires no advance configuration and uses explicit, case insensitive glob matching to remove sensitive data while preserving non sensitive information. -
Filter data using regular expressions.. In C++,
<regex>may throw exceptions, which are not permitted in HotSpot. In Java, regex filtering adds startup cost and allocation, and it complicates extending filtering to new fields or event types in the JVM.
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