Complete guide to understanding, exploiting, and preventing insecure data storage vulnerabilities
Insecure Data Storage occurs when sensitive information is stored without adequate protection, making it accessible to unauthorized parties. This vulnerability affects applications, databases, file systems, cloud storage, and mobile devices. When data is improperly secured, attackers can:
Insecure Data Storage is a persistent vulnerability in the OWASP Mobile Top 10 (#2 in 2024) and contributes to various OWASP Web Top 10 categories. It's critical because:
Storing passwords without hashing or encryption:
# ❌ VULNERABLE: Plaintext storage
def create_user(username, password):
db.execute("INSERT INTO users (username, password) VALUES (?, ?)",
(username, password)) # Password stored as plain text!
# ❌ VULNERABLE: Simple encoding is NOT encryption
import base64
encoded = base64.b64encode(password.encode()) # Easily reversed!
Using outdated or broken encryption algorithms:
# ❌ VULNERABLE: Using DES (deprecated)
from Crypto.Cipher import DES
cipher = DES.new(key, DES.MODE_ECB) # ECB mode is insecure!
# ❌ VULNERABLE: Hardcoded encryption key
SECRET_KEY = "my_secret_key_123" # Key in source code!
Mobile and web applications storing sensitive data insecurely:
// ❌ VULNERABLE: Web browser localStorage (not encrypted)
localStorage.setItem('authToken', token);
localStorage.setItem('creditCard', cardNumber);
// ❌ VULNERABLE: Mobile app shared preferences (plaintext)
SharedPreferences prefs = context.getSharedPreferences("MyApp", MODE_PRIVATE);
prefs.edit().putString("password", userPassword).commit();
Database backups left accessible:
# Attackers look for backup files:
database.sql.bak
users.db.old
backup_2024.sql
db_dump.sql
.git/config # Git repositories with sensitive data
# ❌ VULNERABLE: Public S3 bucket
aws s3 ls s3://company-backups --no-sign-request
# Returns: database-backup-2024.sql
# ❌ VULNERABLE: Azure blob with public access
https://mystorageaccount.blob.core.windows.net/backups/users.db
Extracting data from application memory:
# Android memory dump
adb shell
run-as com.vulnerable.app
cat /data/data/com.vulnerable.app/databases/users.db
# Extract app data (rooted device)
adb root
adb pull /data/data/com.example.app/
# SQLite database extraction
adb pull /data/data/com.example.app/databases/userdata.db
sqlite3 userdata.db "SELECT * FROM users;"
# Shared preferences (XML files with plaintext data)
adb pull /data/data/com.example.app/shared_prefs/
cat *.xml
# Jailbroken iOS device
ssh root@device-ip
cd /var/mobile/Containers/Data/Application/[APP-ID]/
# Keychain extraction (if improperly protected)
keychain_dumper -a
# NSUserDefaults (plist files)
plutil -p Library/Preferences/com.example.app.plist
# Common bucket naming patterns
aws s3 ls s3://companyname-backups --no-sign-request
aws s3 ls s3://companyname-prod --no-sign-request
aws s3 ls s3://companyname-data --no-sign-request
# Automated enumeration
bucket_finder.py companyname wordlist.txt
# Download exposed data
aws s3 sync s3://exposed-bucket ./ --no-sign-request
# Enumerate containers
https://[account].blob.core.windows.net/[container]?restype=container&comp=list
# Access public blobs
https://mystorageaccount.blob.core.windows.net/backups/?comp=list
# Open database
sqlite3 app_database.db
# List tables
.tables
# Extract user credentials
SELECT username, password, email FROM users;
# Check for encryption
PRAGMA cipher_version; # If empty, database is unencrypted
# Recover deleted data
SELECT * FROM users WHERE rowid NOT IN (SELECT rowid FROM users);
# Search for credentials in config files
grep -r "password" /path/to/app/
grep -r "api_key" /path/to/app/
grep -r "secret" /path/to/app/
# Common credential patterns
grep -rE "(password|passwd|pwd|api_key|secret_key|access_token)" .
# ❌ VULNERABLE: Logging sensitive data
import logging
logging.info(f"User login: username={username}, password={password}")
logging.debug(f"API request with token: {auth_token}")
logging.error(f"Database connection failed: {connection_string}")
# All of this ends up in log files accessible to attackers!
# Browser cache
~/.cache/google-chrome/Default/Cache/
~/Library/Caches/ # macOS
# Application cache
/data/data/com.example.app/cache/ # Android
/var/mobile/Containers/Data/Application/[ID]/Library/Caches/ # iOS
# Search cache for sensitive data
grep -r "password\|credit\|ssn\|token" cache_directory/
# "Encrypted" with base64 (not real encryption!)
import base64
# Attacker simply decodes:
encoded_password = "cGFzc3dvcmQxMjM="
decoded = base64.b64decode(encoded_password).decode()
# Result: "password123"
# Simple rotation cipher
import codecs
# Easily reversed:
obfuscated = "frperg_cnffjbeq"
revealed = codecs.decode(obfuscated, 'rot_13')
# Result: "secret_password"
# XOR with hardcoded key (reversible)
def xor_decrypt(encrypted, key):
return ''.join(chr(c ^ ord(key[i % len(key)]))
for i, c in enumerate(encrypted))
# Attacker extracts key from app code and decrypts
# Decompile Android APK
apktool d application.apk
jadx application.apk
# Search for keys in decompiled code
grep -r "AES\|DES\|KEY\|SECRET" .
grep -r "0x[0-9a-fA-F]\{32,\}" . # Hex keys
# iOS binary analysis
class-dump application.app/application
strings application | grep -i "key\|secret\|password"
# Backup extraction (no root needed)
adb backup -f backup.ab -noapk com.example.app
dd if=backup.ab bs=24 skip=1 | openssl zlib -d > backup.tar
tar -xvf backup.tar
# Exploit world-readable files
adb shell
run-as com.example.app
ls -la # Check file permissions
cat databases/sensitive.db # If readable
# Common patterns for bucket discovery
company-name-prod
company-name-backup
company-name-data
companyname-logs
www-companyname-com
# Automated tools
cloud_enum.py -k company-name
s3scanner scan -b bucket-wordlist.txt
✅ THE PRIMARY DEFENSE FOR PASSWORDS
# ✅ GOOD: Using bcrypt (Python)
import bcrypt
# Hash password
password = "user_password"
salt = bcrypt.gensalt(rounds=12) # Cost factor
hashed = bcrypt.hashpw(password.encode(), salt)
# Verify password
if bcrypt.checkpw(password.encode(), hashed):
print("Password correct!")
# ✅ GOOD: Using Argon2 (recommended)
from argon2 import PasswordHasher
ph = PasswordHasher()
hash = ph.hash("user_password")
# Verify
try:
ph.verify(hash, "user_password")
print("Password correct!")
except:
print("Wrong password")
// ✅ GOOD: Using bcrypt (Java)
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
String hashedPassword = encoder.encode("user_password");
// Verify
boolean matches = encoder.matches("user_password", hashedPassword);
# ✅ GOOD: AES-256-GCM with proper key derivation
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
import os
# Derive key from password
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=os.urandom(16),
iterations=100000,
)
key = kdf.derive(b"user_password")
# Encrypt with AES-GCM (authenticated encryption)
iv = os.urandom(12)
cipher = Cipher(algorithms.AES(key), modes.GCM(iv))
encryptor = cipher.encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
tag = encryptor.tag # Authentication tag
# ✅ GOOD: Key from environment
import os
ENCRYPTION_KEY = os.environ.get('ENCRYPTION_KEY')
if not ENCRYPTION_KEY:
raise ValueError("ENCRYPTION_KEY not set!")
# ✅ BEST: Using AWS KMS
import boto3
kms = boto3.client('kms')
# Encrypt data
response = kms.encrypt(
KeyId='arn:aws:kms:region:account:key/key-id',
Plaintext=sensitive_data
)
ciphertext = response['CiphertextBlob']
# Decrypt data
response = kms.decrypt(CiphertextBlob=ciphertext)
plaintext = response['Plaintext']
// ✅ GOOD: Android Keystore
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
KeyGenerator keyGen = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGen.init(new KeyGenParameterSpec.Builder(
"MyKeyAlias",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.build());
SecretKey key = keyGen.generateKey();
// ✅ GOOD: iOS Keychain
import Security
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "userToken",
kSecValueData as String: tokenData,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
// ✅ GOOD: S3 bucket policy (block public access)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket/*",
"arn:aws:s3:::my-bucket"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
# ✅ Enable S3 encryption
aws s3api put-bucket-encryption \
--bucket my-bucket \
--server-side-encryption-configuration \
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'
# ✅ Block public access
aws s3api put-public-access-block \
--bucket my-bucket \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
// ✅ GOOD: File protection
let data = sensitiveData.data(using: .utf8)!
try data.write(to: fileURL, options: .completeFileProtection)
// ✅ GOOD: Encrypted shared preferences
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;
MasterKey masterKey = new MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
context,
"secret_shared_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
sharedPreferences.edit().putString("token", authToken).apply();
# ✅ GOOD: Secure file deletion
import os
def secure_delete(filepath):
# Overwrite file multiple times before deletion
length = os.path.getsize(filepath)
with open(filepath, 'br+') as f:
for _ in range(3):
f.seek(0)
f.write(os.urandom(length))
os.remove(filepath)
# ❌ BAD
logging.info(f"User {username} logged in with password {password}")
# ✅ GOOD
logging.info(f"User {username} logged in successfully")
# ✅ GOOD: Redact sensitive data
def redact_sensitive(data):
return data[:4] + "****" + data[-4:] if len(data) > 8 else "****"
logging.info(f"Processing card {redact_sensitive(card_number)}")
# ✅ GOOD: SQLCipher for encrypted SQLite
import sqlcipher3 as sqlite3
conn = sqlite3.connect('encrypted.db')
conn.execute("PRAGMA key='your-encryption-key'")
conn.execute("PRAGMA cipher_page_size=4096")
-- ✅ SQL Server TDE
USE master;
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'StrongPassword123!';
CREATE CERTIFICATE TDECert WITH SUBJECT = 'TDE Certificate';
USE MyDatabase;
CREATE DATABASE ENCRYPTION KEY
WITH ALGORITHM = AES_256
ENCRYPTION BY SERVER CERTIFICATE TDECert;
ALTER DATABASE MyDatabase SET ENCRYPTION ON;
# Android - Extract and analyze APK
apktool d app.apk
jadx app.apk -d output_dir
# Search for insecure storage
grep -r "SharedPreferences" output_dir/
grep -r "MODE_WORLD_READABLE\|MODE_WORLD_WRITABLE" output_dir/
grep -r "openFileOutput.*MODE_WORLD" output_dir/
# Check for hardcoded secrets
grep -ri "password\|secret\|api_key\|token" output_dir/
grep -rE "[A-Za-z0-9]{32,}" output_dir/ # Potential keys
# iOS - Analyze app bundle
class-dump App.app/App > headers.txt
strings App.app/App | grep -i "password\|secret\|key"
otool -L App.app/App # Check for security frameworks
# Check SQLite encryption
sqlite3 database.db
PRAGMA cipher_version;
# If no output, database is UNENCRYPTED!
# Check for sensitive data
.tables
SELECT * FROM users LIMIT 5;
SELECT * FROM credentials LIMIT 5;
# Android device
adb shell
run-as com.example.app
ls -la databases/
ls -la shared_prefs/
# Check for world-readable (r--r--r--) files
# Look for backup files
find /data/data/com.example.app/ -name "*.bak"
find /data/data/com.example.app/ -name "*.old"
# Test S3 bucket permissions
aws s3 ls s3://target-bucket --no-sign-request
# Test public access
curl https://target-bucket.s3.amazonaws.com/
# Azure storage
curl "https://account.blob.core.windows.net/container/?restype=container&comp=list"
# MobSF (Mobile Security Framework)
# Automated Android/iOS security analysis
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf
# Upload APK/IPA and scan for:
# - Insecure storage
# - Hardcoded secrets
# - Weak encryption
# - Improper file permissions
# Drozer (Android)
drozer console connect
run app.package.info -a com.example.app
run app.provider.finduri com.example.app
run scanner.provider.finduris -a com.example.app
# Trufflehog - Find secrets in code
trufflehog filesystem /path/to/code --json
# GitLeaks - Detect secrets in Git repos
gitleaks detect --source /path/to/repo
# Semgrep - Pattern-based code scanner
semgrep --config=p/owasp-top-ten /path/to/code
# Custom grep patterns
grep -rE "(password|passwd|pwd|api_key|secret|token)\s*=\s*['\"][^'\"]{8,}" .
# ScoutSuite - Multi-cloud security auditing
scout aws --profile myprofile
# Prowler - AWS security assessment
prowler aws -M csv,html
# CloudSploit - Cloud security scanning
./index.js --cloud aws --config config.json
# Search for vulnerable patterns
# Plaintext password storage
grep -r "password.*=.*request\|request.*password" .
grep -r "INSERT.*password.*VALUES" .
# Weak encryption
grep -ri "DES\|RC4\|MD5\|SHA1" .
grep -r "MODE_ECB" .
# Hardcoded secrets
grep -rE "(api_key|secret|password)\s*=\s*['\"][^'\"]{8,}" .
grep -rE "['\"][A-Za-z0-9+/]{40,}={0,2}['\"]" . # Base64 secrets
# Insecure storage
grep -r "localStorage.setItem" . # Web
grep -r "SharedPreferences.*MODE_WORLD" . # Android
grep -r "NSUserDefaults.*string" . # iOS (check if encrypted)
# Logging sensitive data
grep -r "log.*password\|log.*token\|log.*secret" .
# ❌ Plaintext password storage
password = request.form['password']
db.execute("INSERT INTO users (password) VALUES (?)", (password,))
# ❌ Weak hashing (MD5/SHA1)
import hashlib
hashed = hashlib.md5(password.encode()).hexdigest()
# ❌ Hardcoded encryption key
KEY = "my_secret_key_1234567890123456"
# ❌ Insecure local storage
localStorage.setItem('authToken', token);
# ❌ World-readable files (Android)
openFileOutput("data.txt", MODE_WORLD_READABLE);
# ❌ Logging sensitive data
logging.info(f"Password: {password}, Token: {token}")
# ✅ Proper password hashing (Argon2)
from argon2 import PasswordHasher
ph = PasswordHasher()
hash = ph.hash(password)
# ✅ Secure encryption (AES-256-GCM)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
# ✅ Environment-based secrets
import os
SECRET_KEY = os.environ['SECRET_KEY']
# ✅ Secure mobile storage (Android)
import androidx.security.crypto.EncryptedSharedPreferences
# ✅ Secure keychain (iOS)
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
# ✅ Secure cloud storage (S3)
BlockPublicAccess=true, ServerSideEncryption=AES256