Cleartext Storage of Sensitive Information in GUI
Description
Cleartext Storage of Sensitive Information in GUI is a vulnerability that occurs when a product stores sensitive information in cleartext within graphical user interface components. GUI elements such as dialog boxes, text fields, password fields (with incorrect masking), list boxes, and window properties can contain sensitive data that may be accessible through GUI manipulation APIs, screen capture, remote desktop sessions, or accessibility tools. Even when information appears visually hidden (such as password dots), the underlying data may be stored in cleartext within the GUI component's memory or properties, accessible through programmatic inspection.
Risk
Cleartext GUI storage exposes sensitive data through multiple attack vectors specific to graphical environments. Attackers can use accessibility APIs to read content from GUI elements, including password fields that appear masked. Screen-reading software designed for accessibility can extract text from UI components. Remote desktop and screen-sharing sessions may expose GUI content to viewers. GUI automation tools and testing frameworks can read element properties. Malware can scrape GUI content using system APIs designed for window management. Memory inspection of GUI processes reveals stored sensitive data. The risk is amplified on shared or compromised systems where multiple users or applications may have access to GUI component properties.
Solution
Never store sensitive information in cleartext within GUI components, even when visually masked. For password fields, ensure the underlying data structure also protects the content, not just the display. Use secure string types that prevent casual memory inspection. Clear sensitive data from GUI components immediately after use. Implement secure password entry mechanisms that don't retain cleartext in memory. Consider using hardware-backed secure input methods where available. Disable clipboard access for sensitive fields to prevent copy operations. Implement GUI component security testing to verify sensitive data protection. Use OS-provided secure credential input dialogs when available rather than custom password fields.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Attackers can access GUI data through APIs, accessibility tools, or memory inspection, reading sensitive information even when it appears visually hidden or masked in the interface. |
Example Code
Vulnerable Code (C#/Java)
The following examples demonstrate cleartext GUI storage vulnerabilities:
// Vulnerable: Password stored in GUI component properties
using System;
using System.Windows.Forms;
public class VulnerableLoginForm : Form
{
private TextBox usernameTextBox;
private TextBox passwordTextBox; // Regular TextBox, not masked!
private string storedPassword;
public VulnerableLoginForm()
{
usernameTextBox = new TextBox();
usernameTextBox.Location = new Point(10, 10);
// Vulnerable: Using regular TextBox for password
passwordTextBox = new TextBox();
passwordTextBox.Location = new Point(10, 40);
// Password visible in plaintext!
Controls.Add(usernameTextBox);
Controls.Add(passwordTextBox);
}
// Vulnerable: Storing password in form property
public void StoreCredentials(string username, string password)
{
usernameTextBox.Text = username;
// Vulnerable: Password stored in cleartext in TextBox
passwordTextBox.Text = password;
// Vulnerable: Also stored in form variable
storedPassword = password;
// Accessible via reflection or memory inspection
}
// Vulnerable: Password in dialog visible to screen readers
public void ShowPasswordDialog()
{
using (Form dialog = new Form())
{
TextBox passBox = new TextBox();
passBox.Text = storedPassword; // Cleartext in dialog!
dialog.Controls.Add(passBox);
dialog.ShowDialog();
}
}
}
// Vulnerable: Java Swing password handling
import javax.swing.*;
import java.awt.*;
public class VulnerableLoginPanel extends JPanel {
private JTextField usernameField;
private JTextField passwordField; // Vulnerable: Not JPasswordField!
public VulnerableLoginPanel() {
usernameField = new JTextField(20);
// Vulnerable: Using JTextField instead of JPasswordField
passwordField = new JTextField(20);
// Password visible in plaintext in the GUI!
add(new JLabel("Username:"));
add(usernameField);
add(new JLabel("Password:"));
add(passwordField);
}
// Vulnerable: Exposing password through tooltip
public void setPasswordHint(String password) {
// Vulnerable: Password in tooltip!
passwordField.setToolTipText("Your password is: " + password);
}
// Vulnerable: Storing password in accessible property
public void storeCredentials(String username, String password) {
usernameField.setText(username);
passwordField.setText(password); // Cleartext!
// Vulnerable: Also storing in client property
passwordField.putClientProperty("stored_password", password);
}
}
// Vulnerable: Dialog with cleartext password display
public class VulnerablePasswordDialog {
public void showStoredPassword(String password) {
// Vulnerable: Password shown in dialog
JOptionPane.showMessageDialog(
null,
"Your password is: " + password, // Cleartext in GUI!
"Password Reminder",
JOptionPane.INFORMATION_MESSAGE
);
}
}
# Vulnerable: Python tkinter password handling
import tkinter as tk
class VulnerableLoginWindow:
def __init__(self, root):
self.root = root
self.stored_password = None
# Vulnerable: Password field without masking
self.username_var = tk.StringVar()
self.password_var = tk.StringVar()
tk.Label(root, text="Username:").pack()
self.username_entry = tk.Entry(root, textvariable=self.username_var)
self.username_entry.pack()
tk.Label(root, text="Password:").pack()
# Vulnerable: No show='*' to mask password
self.password_entry = tk.Entry(root, textvariable=self.password_var)
self.password_entry.pack()
def save_credentials(self, username, password):
# Vulnerable: Storing cleartext in GUI variables
self.username_var.set(username)
self.password_var.set(password)
# Vulnerable: Also storing in instance variable
self.stored_password = password
def show_password_reminder(self):
# Vulnerable: Showing cleartext password in message box
tk.messagebox.showinfo(
"Password Reminder",
f"Your password is: {self.stored_password}"
)
Fixed Code (C#/Java)
// Fixed: Secure password handling in GUI
using System;
using System.Windows.Forms;
using System.Security;
using System.Runtime.InteropServices;
public class SecureLoginForm : Form
{
private TextBox usernameTextBox;
private MaskedTextBox passwordTextBox; // Fixed: Masked input
private SecureString securePassword;
public SecureLoginForm()
{
usernameTextBox = new TextBox();
usernameTextBox.Location = new Point(10, 10);
// Fixed: Use proper password masking
passwordTextBox = new MaskedTextBox();
passwordTextBox.PasswordChar = '*';
passwordTextBox.Location = new Point(10, 40);
// Fixed: Disable copy/paste for password field
passwordTextBox.ShortcutsEnabled = false;
Controls.Add(usernameTextBox);
Controls.Add(passwordTextBox);
}
// Fixed: Using SecureString for password storage
public void HandleLogin()
{
string username = usernameTextBox.Text;
// Fixed: Convert to SecureString immediately
securePassword = new SecureString();
foreach (char c in passwordTextBox.Text)
{
securePassword.AppendChar(c);
}
securePassword.MakeReadOnly();
// Fixed: Clear the text box immediately
passwordTextBox.Clear();
// Authenticate using secure password
bool result = AuthenticateSecure(username, securePassword);
// Fixed: Dispose SecureString after use
securePassword.Dispose();
securePassword = null;
}
private bool AuthenticateSecure(string username, SecureString password)
{
IntPtr passwordPtr = IntPtr.Zero;
try
{
passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(password);
// Use password briefly for authentication
return VerifyCredentials(username, passwordPtr);
}
finally
{
// Fixed: Clear unmanaged memory
if (passwordPtr != IntPtr.Zero)
{
Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);
}
}
}
// Fixed: Never show password in dialogs
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Fixed: Clear sensitive data on form close
passwordTextBox?.Clear();
securePassword?.Dispose();
}
base.Dispose(disposing);
}
}
// Fixed: Secure Java Swing password handling
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
public class SecureLoginPanel extends JPanel {
private JTextField usernameField;
private JPasswordField passwordField; // Fixed: Use JPasswordField
public SecureLoginPanel() {
usernameField = new JTextField(20);
// Fixed: JPasswordField masks input and uses char[]
passwordField = new JPasswordField(20);
// Default echo char is bullet
add(new JLabel("Username:"));
add(usernameField);
add(new JLabel("Password:"));
add(passwordField);
}
// Fixed: Secure credential handling
public void handleLogin() {
String username = usernameField.getText();
// Fixed: Get password as char array (mutable, clearable)
char[] password = passwordField.getPassword();
try {
// Authenticate using password
boolean result = authenticate(username, password);
if (result) {
showSuccess();
} else {
showError();
}
} finally {
// Fixed: Clear password from memory
Arrays.fill(password, '\0');
// Fixed: Clear password field
passwordField.setText("");
}
}
// Fixed: Never store password in client properties
// Fixed: Never show password in tooltips or dialogs
private void showSuccess() {
JOptionPane.showMessageDialog(
this,
"Login successful!",
"Success",
JOptionPane.INFORMATION_MESSAGE
);
}
private void showError() {
JOptionPane.showMessageDialog(
this,
"Invalid credentials", // Fixed: Don't reveal password
"Login Failed",
JOptionPane.ERROR_MESSAGE
);
}
private boolean authenticate(String username, char[] password) {
// Implement secure authentication
return false;
}
}
# Fixed: Secure Python tkinter password handling
import tkinter as tk
from tkinter import messagebox
import gc
class SecureLoginWindow:
def __init__(self, root):
self.root = root
tk.Label(root, text="Username:").pack()
self.username_entry = tk.Entry(root)
self.username_entry.pack()
tk.Label(root, text="Password:").pack()
# Fixed: Use show='*' to mask password
self.password_entry = tk.Entry(root, show='*')
self.password_entry.pack()
tk.Button(root, text="Login", command=self.handle_login).pack()
def handle_login(self):
username = self.username_entry.get()
password = self.password_entry.get()
try:
# Authenticate
result = self.authenticate(username, password)
if result:
messagebox.showinfo("Success", "Login successful!")
else:
# Fixed: Don't reveal password in error
messagebox.showerror("Error", "Invalid credentials")
finally:
# Fixed: Clear password field immediately
self.password_entry.delete(0, tk.END)
# Fixed: Clear password variable (best effort in Python)
password = None
gc.collect()
def authenticate(self, username, password):
# Implement authentication
return False
# Fixed: Never show password in dialogs
# Fixed: Never store password in instance variables
The fix uses proper password fields with masking, SecureString where available, and clears sensitive data after use.
Exploited in the Wild
Dialog Password Exposure (Desktop Applications, 2002)
CVE-2002-1848 documented applications storing unencrypted passwords in GUI dialogs, allowing local users to access passwords through GUI inspection tools.
Accessibility Tool Credential Theft (Various, Ongoing)
Attackers have used accessibility APIs and screen readers to extract credentials from password fields that only mask display but store cleartext internally.
Tools to Test/Exploit
-
UI Spy — Windows tool for inspecting UI automation properties.
-
Accessibility Insights — Tool that can reveal GUI element properties including hidden text.
-
Spy++ — Windows tool for examining window properties and messages.
CVE Examples
- CVE-2002-1848 — Unencrypted passwords stored in GUI dialog.
References
-
MITRE Corporation. "CWE-317: Cleartext Storage of Sensitive Information in GUI." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/317.html
-
Microsoft. "UI Automation Security Overview." https://docs.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-security-overview
-
OWASP Foundation. "Secure Coding Practices." https://owasp.org/