VNX-591: Password read into a char[] copied into an immutable String
Reports Java code that copies a password returned as char[] (Console.readPassword, JPasswordField/PasswordCallback/KeyStore.PasswordProtection getPassword) into an immutable String, or reads JPasswordField.getText(), so the secret can no longer be wiped from memory.
| Field | Value |
|---|---|
| Rule ID | VNX-591 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Java |
| CWE | CWE-591 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | java password memory sensitive-data cwe-591 |
| Source | internal/sast/rules/vnx-591.rego |
Overview
VNX-591 maps to CWE-591: Sensitive Data Storage in Improperly Locked Memory. Java cannot lock memory against paging or core and heap dumps. The one control the platform offers for a password is to keep it in a char[] and overwrite the array once it has been used. The JDK’s password APIs return char[] for exactly this reason.
The rule reports the line that throws that control away: new String(...), String.valueOf(...) or String.copyValueOf(...) applied to Console.readPassword(), to getPassword() of JPasswordField, PasswordCallback or KeyStore.PasswordProtection, or to a char[] variable assigned from one of those within the 15 lines above. It also reports the deprecated JPasswordField.getText(), which returns a String.
It does not report a char[] that is used and wiped without becoming a String, a getPassword() of an application class that already returns a String, new String over other byte or char arrays, or comments and string literals. Native memory locking (mlock, VirtualLock) in C/C++ is not analysed.
Why This Matters
A String is immutable and is collected whenever the garbage collector chooses. Until then the password sits in the heap, where it can be paged to swap, written into a heap dump or crash dump, or read by anything that can inspect the process. A char[] can be cleared the moment authentication finishes.
What Gets Flagged
// FLAGGED: the password becomes an immutable String
String password = new String(console.readPassword("Password: "));
char[] secret = console.readPassword("Keystore password: ");
String asText = new String(secret); // FLAGGED
String typed = new String(passwordField.getPassword()); // FLAGGED
String pin = pinField.getText(); // FLAGGED: JPasswordField.getText()
// NOT FLAGGED: the char[] is used and wiped
char[] pw = console.readPassword("Password: ");
try {
verify(pw);
} finally {
Arrays.fill(pw, '\0');
}
Remediation
- Keep the password in the
char[]the API returned and pass that array to the code that needs it (PBEKeySpec,KeyStore.load,KeyManagerFactory.initall takechar[]). - Overwrite the array with
Arrays.fill(pw, '\0')in afinallyblock once it has been used. - Replace
JPasswordField.getText()withgetPassword().
References
- CWE-591: Sensitive Data Storage in Improperly Locked Memory
- Java Cryptography Architecture: why passwords are char arrays
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-591
// nosec vnx-591
Or record an ignore rule: vulnetix ignore add --rule vnx-591 --reason "...". See Suppressing findings.