Even when you store secrets correctly at rest, they spend time in memory while the app uses them, and memory is its own exposure surface. A password, key, or token sitting in RAM can be captured in a memory dump, a crash log, or by an attacker with runtime access on a compromised device. You cannot make this risk zero on a managed platform, but you can shrink it: minimize how long sensitive data lives in memory and clear it when you are done. Here is why sensitive data in memory is a risk, where it gets exposed, and how to handle secrets in memory.
Short answer
Sensitive data held in memory, passwords, keys, tokens, can be exposed through memory dumps, crash logs, or an attacker with runtime access on a compromised device, so it is worth minimizing how long it lives in memory and clearing it after use. Per OWASP MASVS, the practical steps are to keep sensitive data in memory only as briefly as needed, prefer clearable buffers like byte arrays over immutable strings for secrets where you can, zero them after use, and avoid logging or unnecessary copies. This is defense-in-depth and partial on managed languages, since you cannot fully control memory there, but it raises the bar and reduces the window in which a secret is exposed.
What you should know
- Memory is an exposure surface: secrets in RAM can be captured.
- Dumps and crash logs leak it: memory can end up in a report or dump.
- Compromised devices expose runtime memory: an attacker can read it.
- Minimize lifetime, clear after use: shrink the window of exposure.
- It is partial on managed languages: defense-in-depth, not absolute.
Why is sensitive data in memory a risk?
Because memory can be read, captured, or persisted beyond the moment of use. While your app holds a secret in memory to use it, that value exists in RAM, and several things can expose it: a memory dump or heap inspection, a crash report that captures memory, and, on a compromised, jailbroken, or rooted device, an attacker with runtime access reading the app's memory directly with tools like a debugger or instrumentation framework. The longer a secret lives in memory and the more copies of it exist, the larger this window. Managed languages add a wrinkle: values like strings are often immutable and managed by a garbage collector, so a secret in a string can linger in memory after you are done with it, beyond your control to clear immediately. So even with correct storage at rest, the in-memory lifetime of a secret is a real, if often overlooked, exposure.
Where does it get exposed?
In a few specific places. The table lists them.
| Exposure | How the secret leaks |
|---|---|
| Memory or heap dump | Captured RAM includes the secret |
| Crash logs | A crash report may contain memory contents |
| Runtime access on a compromised device | An attacker reads the app's live memory |
| Lingering immutable strings | A secret in a string stays in memory after use |
| Unnecessary copies | Each copy of the secret is another exposure |
The recurring theme is that a secret in memory is not confined to the instant you use it: it can be captured if the process is dumped or crashes, it can be read on a device the attacker controls, and on managed platforms it can persist longer than intended. Each unnecessary copy and each extra moment in memory adds to the risk, which is why minimizing lifetime and clearing after use matter.
How do you handle secrets in memory?
Minimize lifetime, use clearable buffers, and zero them after use. Keep a secret in memory only for as long as you actually need it, fetch it, use it, and release it, rather than holding it for the app's lifetime. Where the platform allows, prefer a mutable, clearable buffer such as a byte array over an immutable string for a secret, so you can overwrite it with zeros when done rather than leaving a copy the runtime manages, since immutable strings cannot be reliably cleared. Avoid making unnecessary copies of the secret, and never log it or include it in error reports. Recognize the limits: on managed languages and especially on a compromised device, you cannot guarantee a secret is unrecoverable from memory, so this is defense-in-depth that reduces the window, not an absolute control. Combine it with keeping secrets off the device where possible and protecting them at rest. The principle is to give a secret the shortest, smallest memory footprint you can.
What to watch out for
The first trap is holding secrets in memory for the whole app lifetime when they are needed briefly; minimize their lifetime. The second is keeping secrets in immutable strings that cannot be cleared, leaving copies in memory; use clearable buffers where feasible and zero them. The third is logging a secret or including it in a crash report. Memory hygiene is code-level and partial, so a pre-submission scan such as PTKD.com (https://ptkd.com), which reads the binary against OWASP MASVS, focuses on storage at rest, secrets, and logging rather than runtime memory, but it catches the related issues like secrets in storage or logs. The in-memory handling you implement in your code.
What to take away
- Sensitive data in memory can be exposed through dumps, crash logs, or an attacker with runtime access on a compromised device.
- Minimize how long secrets live in memory, prefer clearable buffers like byte arrays over immutable strings, zero them after use, and avoid copies and logging.
- This is defense-in-depth and partial on managed languages, since you cannot fully control memory there, but it shrinks the exposure window.
- Pair it with keeping secrets off the device and protecting them at rest, and use a pre-submission scan such as PTKD.com to catch secrets in storage or logs.



