Your Java Heap Dumps Contain Plain-Text Passwords
How to find and remove sensitive data from .hprof files before they become a security incident.
The tool we use in this tutorial, hprof-redact, was created by Johannes Bechberger. If you follow JVM internals, performance tooling, or OpenJDK discussions, you probably know his work already. His original write-up on redacting heap dumps is available on his blog, I’ve taken the code examples from there too.
It was a real pleasure to meet Johannes at Jfokus and talk about JVM tooling, observability, and all the small details that most people never think about. Tools like this exist because someone actually cares about what happens in production at 2am.
Now let’s make heap dumps safer.
Most Java developers treat heap dumps as harmless debugging artifacts. You capture a .hprof file, open it in Eclipse MAT, look for retained objects, and fix your memory leak. End of story.
But a heap dump is not just object graphs and allocation statistics. It is a raw snapshot of your JVM memory. If a password, API token, OAuth secret, or personal data exists in memory at that moment, it is in the dump. In plain text. Exactly as your application stored it.
In production, this becomes a real problem. You open a support ticket and attach the heap dump. You upload it to a third-party analysis tool. You copy it to a shared file server. Now your secrets live outside your security boundary. At 2am, when you’re debugging a leak, this risk is easy to forget.
The naive mental model is: “Heap dumps are internal technical files.” The production reality is: heap dumps are data exfiltration risks. If you do not sanitize them, you leak credentials by design.
In this tutorial, we build a small Java application that holds sensitive data in memory. We generate a heap dump. We prove the secret is visible. Then we use hprof-redact to scrub the file, both from the CLI and programmatically. After this, you can share heap dumps without sharing your secrets.
Prerequisites
You need a recent JDK and basic familiarity with compiling and running Java from the command line.
Java 21 or newer installed
Basic knowledge of the
javacandjavacommandsFamiliarity with
jmapJBang installed (for running the redaction CLI)
Create a Java App That Stores Sensitive Data
We start with a very simple application. It creates an object that contains a username and a password. Then it sleeps forever so we can capture a heap dump.
Let’s create a simple folder to keep our experiments:
mkdir heap-pii-demoCreate a file named SecretFieldTest.java:
record Configuration(String user, String password) {}
public class SecretFieldTest {
public static void main(String[] args) throws InterruptedException {
Configuration config =
new Configuration("admin-user", "SuperSecretToken2026!");
System.out.println("Application is running.");
System.out.println("PID: " + ProcessHandle.current().pid());
System.out.println("Press Ctrl+C to exit.");
Thread.sleep(Long.MAX_VALUE);
}
}Compile and run it:
javac -d out SecretFieldTest.java
java -cp out SecretFieldTest &The program prints the PID. You need this in the next step.
This code does nothing fancy. It just creates a Configuration record with a plain-text password. That is enough. If the object is on the heap, the secret is in the heap dump. There is no special handling, no encryption, no protection.
This is important: the JVM does not treat your secrets differently. A String with a password is just a char[] internally. The dump will contain it exactly as stored.
Step 2: Generate a Heap Dump
Now we capture the memory snapshot using jmap, which ships with the JDK.
Replace <PID> with the process ID printed by your application:
jmap -dump:file=leak.hprof <PID>You should see output confirming that the heap dump was written.
You now have a file named leak.hprof in your directory. This file contains:
All objects
All strings
All primitive fields
All object references
This is not a filtered diagnostic view. It is raw memory state serialized in HPROF format.
Step 3: Prove That the Secret Is Exposed
Before we redact anything, we prove the problem.
You can use a full heap analysis tool, but for a quick check, grep is enough. Since .hprof is binary, use the -a flag:
grep -a 'SuperSecretToken2026!' /path/to/leak.hprofYou will see your secret in the output.
That means anyone with access to this file can extract your password. No special tools required. A simple string search is enough.
This is where many teams get surprised. They treat heap dumps as technical artifacts. But technically, they are sensitive data containers.
Step 4: Redact the Heap Dump with hprof-redact (CLI)
Now we fix the problem using hprof-redact.
The easiest way to run it is via JBang:
jbang hprof-redact@parttimenerd/hprof-redact leak.hprof redacted.hprofBy default, the tool uses the zero transformer. This means:
All strings are replaced with zero-bytes of the same length
All primitives are zeroed out
Object sizes remain unchanged
Why does size matter? Because memory analysis tools rely on object sizes and graph structure. If you shrink strings or drop fields, your analysis becomes inaccurate. The zero transformer keeps the structure intact.
There are other transformers:
-t zero-strings- zero only strings-t drop-strings- replace strings with empty strings
Now verify the result:
grep -a 'SuperSecretToken2026!' redacted.hprofYou should get no output.
The secret is gone, but the heap structure remains analyzable.
This is the key guarantee: data is wiped, structure is preserved.
The limit: this does not magically re-secure already leaked dumps. You must run it before sharing the file.
Step 5: Custom Redaction with the Java Library
Sometimes you want more control. Maybe you want to:
Replace all strings with
"REDACTED"Mask only specific fields
Replace all integers with a fixed value
For this, you use hprof-redact as a library.
Add the Dependency
Add this to your pom.xml:
<dependency>
<groupId>me.bechberger</groupId>
<artifactId>hprof-redact</artifactId>
<version>0.1.1</version>
</dependency>Implement a Custom Transformer
Create a new class CustomRedactor.java:
package com.example.heap;
import me.bechberger.hprof.HprofRedact;
import me.bechberger.hprof.transformer.HprofTransformer;
import java.io.IOException;
import java.nio.file.Path;
public class CustomRedactor {
public static class MyTransformer implements HprofTransformer {
@Override
public String transformUtf8String(String value) {
return "REDACTED_STRING";
}
@Override
public int transformInt(int value) {
return 42;
}
}
public static void main(String[] args) {
try {
System.out.println("Processing heap dump...");
HprofRedact.process(
Path.of("leak.hprof"),
Path.of("custom_redacted.hprof"),
new MyTransformer()
);
System.out.println("Heap dump redacted successfully!");
} catch (IOException e) {
e.printStackTrace();
}
}
}Run this class and inspect custom_redacted.hprof.
Every string becomes "REDACTED_STRING". Every integer becomes 42.
This approach gives you full control. You can inspect field names, types, or values and apply rules conditionally.
What this guarantees: deterministic transformation of heap content.
What it does not guarantee: semantic correctness of your analysis. If you replace all numbers, memory analysis for numeric-heavy workloads becomes less useful. You need to choose rules carefully.
Production Hardening
Redacting heap dumps is not just a local debugging trick. In production, you need a process.
Automate Redaction Before Upload
Never upload raw .hprof files to support systems. Create a small script:
#!/bin/bash
INPUT=$1
OUTPUT="redacted-$INPUT"
jbang hprof-redact@parttimenerd/hprof-redact "$INPUT" "$OUTPUT"
echo "Redacted file created: $OUTPUT"This removes human error from the process.
Control Heap Dump Generation
On production systems, heap dumps are often triggered automatically on OutOfMemoryError:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/secure/locationMake sure the dump location:
Is not world-readable
Is not automatically synced to shared storage
Is protected by filesystem permissions
A redacted dump is safer. An unprotected dump on a shared volume is a liability.
Understand the Limits
Redaction removes visible strings and primitives. It does not:
Encrypt the file
Protect against tampering
Remove metadata like class names
If your class name is CreditCardProcessor or OAuthTokenStore, that still appears. For most cases this is fine, but do not treat redaction as full anonymization.
Conclusion
Heap dumps are essential for diagnosing memory leaks, but they are also full copies of your application’s memory, including secrets. We created a simple Java application, captured a heap dump, proved that credentials were exposed, and then removed them using hprof-redact from both the CLI and a custom Java transformer. The structure stayed intact, the secrets did not.
From now on, treat every .hprof file as sensitive data, and redact it before it leaves your machine.


