Insecure Storage of Sensitive Information

Description

Insecure Storage of Sensitive Information occurs when software stores sensitive information without properly limiting read or write access by unauthorized actors. This vulnerability encompasses storing sensitive data in cleartext, using weak encryption, setting improper file permissions, or placing data in locations accessible to unintended parties. When read access isn't properly restricted, attackers can steal sensitive information such as credentials, personal data, or cryptographic keys. When write access isn't restricted, attackers can modify or delete data, potentially causing incorrect application behavior, data corruption, or denial of service.

Risk

Insecure storage can lead to severe security breaches. Exposed credentials enable account takeover and lateral movement within systems. Leaked personal data results in privacy violations and regulatory penalties. Compromised cryptographic keys undermine all security measures depending on those keys. Modified configuration data can alter application behavior maliciously. This vulnerability is particularly dangerous because it can persist undetected for extended periods, allowing attackers ongoing access to sensitive data. The risk extends beyond the immediate application to any systems that trust the compromised data.

Solution

Implement a defense-in-depth approach to data storage security. Classify data by sensitivity and apply appropriate protection levels. Use strong encryption for sensitive data at rest with properly managed keys. Store encryption keys separately from encrypted data, preferably in hardware security modules or secure key stores. Apply restrictive file permissions (owner-only where possible). Use platform-provided secure storage mechanisms (Keychain, KeyStore). Never store credentials in cleartext or reversibly encrypted form. Implement access logging for sensitive data. Regularly audit storage permissions and encryption strength. Consider data minimization—don't store sensitive data unless necessary.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Application Data - Attackers can read sensitive information by accessing the insecure storage mechanism.
IntegrityScope: Integrity

Modify Application Data - Attackers can overwrite or corrupt sensitive information in insecure storage.

Example Code

Vulnerable Code

# Vulnerable: Storing credentials in cleartext config file
import json

class VulnerableConfig:

    def save_credentials(self, username, password, api_key):
        config = {
            'username': username,
            'password': password,  # Cleartext password!
            'api_key': api_key      # Cleartext API key!
        }

        # Vulnerable: World-readable file
        with open('/etc/myapp/config.json', 'w') as f:
            json.dump(config, f)
        # Default permissions may be readable by others
// Vulnerable: Storing sensitive data in SharedPreferences without encryption
public class VulnerablePreferences {

    public void saveUserData(Context context, String password, String creditCard) {
        SharedPreferences prefs = context.getSharedPreferences(
            "user_data", Context.MODE_PRIVATE);

        // Vulnerable: Stored in XML in cleartext
        // /data/data/com.app/shared_prefs/user_data.xml
        prefs.edit()
            .putString("password", password)      // Cleartext!
            .putString("credit_card", creditCard) // Cleartext!
            .apply();

        // Root users and backup utilities can access this
    }
}
// Vulnerable: Storing sensitive data in browser storage
class VulnerableStorage {

    saveSession(sessionToken, userEmail, paymentInfo) {
        // Vulnerable: localStorage is accessible to any script
        localStorage.setItem('session_token', sessionToken);
        localStorage.setItem('user_email', userEmail);
        localStorage.setItem('payment_info', JSON.stringify(paymentInfo));

        // XSS attacks can steal all of this
    }
}
// Vulnerable: Storing password in configuration file
public class VulnerableDbConfig
{
    public static void SaveDatabaseConfig(string connectionString)
    {
        // Vulnerable: Connection string with password in app.config
        var config = ConfigurationManager.OpenExeConfiguration(
            ConfigurationUserLevel.None);

        config.ConnectionStrings.ConnectionStrings.Add(
            new ConnectionStringSettings(
                "MainDB",
                // Cleartext: "Server=db;User=admin;Password=secret123;"
                connectionString
            )
        );

        config.Save();
    }
}
// Vulnerable: Cookie storing sensitive data
<?php
function vulnerable_remember_me($userId, $password) {
    // Vulnerable: Storing password in cookie
    setcookie('user_id', $userId, time() + 86400);
    setcookie('password', $password, time() + 86400);  // Cleartext!

    // Cookie transmitted with every request, visible in browser
}

// Vulnerable: Session data on shared hosting
session_save_path('/tmp');  // Shared temp directory
session_start();
$_SESSION['credit_card'] = $creditCard;  // Other users might access
?>
// Vulnerable: Embedding secrets in binary
package main

const (
    // Vulnerable: Hardcoded secrets extractable via strings command
    APIKey       = "sk_live_abc123xyz789"
    DatabasePass = "super_secret_password"
)

func main() {
    // These values are compiled into the binary
    connectDatabase(DatabasePass)
}

Fixed Code

# Fixed: Secure credential storage with encryption
import json
import os
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64
import keyring

class FixedConfig:

    def __init__(self, app_name: str):
        self.app_name = app_name
        self.config_dir = os.path.join(os.path.expanduser('~'), '.config', app_name)
        os.makedirs(self.config_dir, mode=0o700, exist_ok=True)

    def save_credentials(self, username: str, password: str, api_key: str):
        # Fixed: Store password in OS keyring (protected by OS)
        keyring.set_password(self.app_name, 'password', password)

        # Fixed: Store API key in keyring
        keyring.set_password(self.app_name, 'api_key', api_key)

        # Fixed: Only store non-sensitive data in file
        config = {'username': username}

        config_path = os.path.join(self.config_dir, 'config.json')
        with open(config_path, 'w') as f:
            json.dump(config, f)

        # Fixed: Restrictive permissions
        os.chmod(config_path, 0o600)

    def get_password(self) -> str:
        return keyring.get_password(self.app_name, 'password')

    def get_api_key(self) -> str:
        return keyring.get_password(self.app_name, 'api_key')
// Fixed: Using EncryptedSharedPreferences and Android Keystore
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;

public class FixedPreferences {

    private SharedPreferences securePrefs;

    public FixedPreferences(Context context) throws Exception {
        // Fixed: Create master key in Android Keystore
        MasterKey masterKey = new MasterKey.Builder(context)
            .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
            .build();

        // Fixed: Use encrypted SharedPreferences
        securePrefs = EncryptedSharedPreferences.create(
            context,
            "secure_prefs",
            masterKey,
            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
        );
    }

    public void saveUserData(String password, String creditCard) {
        // Fixed: Data is encrypted automatically
        securePrefs.edit()
            .putString("password", password)
            .putString("credit_card", creditCard)
            .apply();
    }

    // For very sensitive data, use Keystore directly
    public void saveTokenInKeystore(String token) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);

        // Store using Keystore protection
        // Token would need to be stored via EncryptedSharedPreferences
        // or similar secure mechanism
    }
}
// Fixed: Secure storage in browser with encryption
class FixedStorage {

    constructor() {
        this.cryptoKey = null;
    }

    async initialize() {
        // Generate or retrieve encryption key
        this.cryptoKey = await this.getOrCreateKey();
    }

    async saveSession(sessionToken, userEmail) {
        // Fixed: Encrypt sensitive data before storage
        const encryptedToken = await this.encrypt(sessionToken);

        // Use sessionStorage for session-only data (cleared on close)
        sessionStorage.setItem('session_token', JSON.stringify(encryptedToken));

        // For less sensitive data, still avoid localStorage if possible
        // Use HttpOnly cookies for session tokens when feasible
    }

    async encrypt(data) {
        const iv = crypto.getRandomValues(new Uint8Array(12));
        const encoded = new TextEncoder().encode(data);

        const encrypted = await crypto.subtle.encrypt(
            { name: 'AES-GCM', iv },
            this.cryptoKey,
            encoded
        );

        return {
            iv: Array.from(iv),
            data: Array.from(new Uint8Array(encrypted))
        };
    }

    async decrypt(encrypted) {
        const decrypted = await crypto.subtle.decrypt(
            { name: 'AES-GCM', iv: new Uint8Array(encrypted.iv) },
            this.cryptoKey,
            new Uint8Array(encrypted.data)
        );

        return new TextDecoder().decode(decrypted);
    }

    async getOrCreateKey() {
        // In production, key management needs careful consideration
        // This is a simplified example
        return await crypto.subtle.generateKey(
            { name: 'AES-GCM', length: 256 },
            false,
            ['encrypt', 'decrypt']
        );
    }
}

// Better: Use HttpOnly secure cookies for session tokens
// Set by server: Set-Cookie: session=xyz; HttpOnly; Secure; SameSite=Strict
// Fixed: Using DPAPI and secure configuration
using System.Security.Cryptography;
using Microsoft.Extensions.Configuration;
using Azure.Security.KeyVault.Secrets;

public class FixedDbConfig
{
    // Fixed: Use environment variables or secret management
    public static string GetConnectionString()
    {
        // Option 1: Environment variables
        string password = Environment.GetEnvironmentVariable("DB_PASSWORD");

        // Option 2: Azure Key Vault
        var client = new SecretClient(
            new Uri("https://mykeyvault.vault.azure.net/"),
            new DefaultAzureCredential());
        KeyVaultSecret secret = client.GetSecret("db-password");
        password = secret.Value;

        // Option 3: DPAPI for local storage
        // password = DecryptWithDpapi(encryptedPassword);

        return $"Server=db;User=admin;Password={password};";
    }

    public static void SaveEncryptedPassword(string password)
    {
        // Fixed: Encrypt with DPAPI before storage
        byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
        byte[] encryptedBytes = ProtectedData.Protect(
            passwordBytes,
            null,
            DataProtectionScope.CurrentUser
        );

        // Store encrypted bytes
        File.WriteAllBytes(GetSecureConfigPath(), encryptedBytes);
    }

    private static string GetSecureConfigPath()
    {
        string appData = Environment.GetFolderPath(
            Environment.SpecialFolder.LocalApplicationData);
        return Path.Combine(appData, "MyApp", "db_creds.bin");
    }
}
// Fixed: Secure session and cookie handling
<?php
function fixed_remember_me($userId) {
    // Fixed: Generate secure random token
    $token = bin2hex(random_bytes(32));
    $tokenHash = password_hash($token, PASSWORD_DEFAULT);

    // Store hash in database, not the token itself
    store_remember_token($userId, $tokenHash, time() + 86400 * 30);

    // Fixed: HttpOnly, Secure, SameSite cookies
    setcookie('remember_token', $token, [
        'expires' => time() + 86400 * 30,
        'path' => '/',
        'secure' => true,      // HTTPS only
        'httponly' => true,    // Not accessible via JavaScript
        'samesite' => 'Strict' // CSRF protection
    ]);
}

// Fixed: Secure session configuration
function configure_secure_session() {
    // Use private session directory
    $sessionDir = '/var/lib/myapp/sessions';
    if (!is_dir($sessionDir)) {
        mkdir($sessionDir, 0700, true);
    }
    session_save_path($sessionDir);

    // Secure session settings
    ini_set('session.cookie_httponly', 1);
    ini_set('session.cookie_secure', 1);
    ini_set('session.use_strict_mode', 1);
    ini_set('session.cookie_samesite', 'Strict');

    session_start();
}
?>
// Fixed: External secret management
package main

import (
    "os"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/secretsmanager"
)

func getSecrets() (string, string, error) {
    // Fixed: Use environment variables
    apiKey := os.Getenv("API_KEY")

    // Fixed: Or use secret manager
    sess := session.Must(session.NewSession())
    svc := secretsmanager.New(sess)

    result, err := svc.GetSecretValue(&secretsmanager.GetSecretValueInput{
        SecretId: aws.String("prod/myapp/database"),
    })
    if err != nil {
        return "", "", err
    }

    databasePass := *result.SecretString
    return apiKey, databasePass, nil
}

CVE Examples

  • CVE-2009-2272: Password and username stored in cleartext in a cookie.

  • CWE-664: Improper Control of a Resource Through its Lifetime (parent)
  • CWE-312: Cleartext Storage of Sensitive Information (child)
  • CWE-921: Storage of Sensitive Data in a Mechanism without Access Control (child)
  • CWE-311: Missing Encryption of Sensitive Data (related)

References

  1. MITRE Corporation. "CWE-922: Insecure Storage of Sensitive Information." https://cwe.mitre.org/data/definitions/922.html
  2. OWASP. "Cryptographic Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
  3. OWASP. "Password Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html