# Insecure Data Storage

## **📋 Índice**

1. [Fundamentos do Data Storage no Android](#-fundamentos-do-data-storage-no-android)
2. [Métodos de Armazenamento](#-métodos-de-armazenamento)
3. [Mecanismos de Ataque](#-mecanismos-de-ataque)
4. [Técnicas de Exploração](#-técnicas-de-exploração)
5. [Vetores de Ataque](#-vetores-de-ataque)
6. [Ferramentas de Exploração](#-ferramentas-de-exploração)
7. [Impacto e Consequências](#-impacto-e-consequências)
8. [Detecção e Monitoramento](#-detecção-e-monitoramento)
9. [Mitigações e Hardening](#-mitigações-e-hardening)
10. [Checklists de Segurança](#-checklists-de-segurança)

***

## 🔍 **Fundamentos do Data Storage no Android**

### **O que é Insecure Data Storage?**

**Insecure Data Storage** refere-se ao armazenamento inadequado de dados sensíveis em aplicações Android. Isso inclui salvar senhas, tokens, informações pessoais, dados financeiros ou qualquer informação confidencial de forma não criptografada ou em locais acessíveis por outros aplicativos ou usuários com acesso físico ao dispositivo.

### **Princípio de Funcionamento**

```mermaid
graph TD
    subgraph "Armazenamento Inseguro"
        A[Dados Sensíveis] --> B[SharedPreferences]
        A --> C[Arquivos Internos]
        A --> D[SQLite Database]
        A --> E[Cache]
        A --> F[Logs]
    end
    
    subgraph "Acesso Não Autorizado"
        B --> G[Dispositivo com Root]
        C --> H[Backup ADB]
        D --> I[Outros Apps]
        E --> J[Análise Forense]
        F --> K[Logcat]
    end
    
    subgraph "Consequências"
        G --> L[Vazamento de Dados]
        H --> L
        I --> L
        J --> L
        K --> L
    end
    
    style A fill:#ff9999
    style L fill:#ff3333
```

### **Hierarquia de Armazenamento**

```yaml
Locais de Armazenamento no Android:

  🔴 External Storage (World-Readable):
    - /sdcard/
    - /storage/emulated/0/
    - Acessível por qualquer app com permissão READ_EXTERNAL_STORAGE

  🟠 Internal Storage (App-Private):
    - /data/data/com.example.app/
    - Teoricamente privado, mas acessível com root
    - Backup via ADB pode expor dados

  🟡 SharedPreferences (XML):
    - /data/data/com.example.app/shared_prefs/
    - Dados em texto plano
    - Acessível com root

  🔵 SQLite Databases:
    - /data/data/com.example.app/databases/
    - Estrutura de tabelas
    - Pode conter dados sensíveis

  🟢 Cache:
    - /data/data/com.example.app/cache/
    - Dados temporários
    - Pode persistir dados sensíveis
```

***

## 💾 **Métodos de Armazenamento**

### **Comparação entre Métodos**

| Método                | Localização | Criptografia Padrão | Acesso Root | Backup ADB |
| --------------------- | ----------- | ------------------- | ----------- | ---------- |
| **SharedPreferences** | Internal    | ❌                   | ✅           | ✅          |
| **Internal Files**    | Internal    | ❌                   | ✅           | ✅          |
| **External Files**    | External    | ❌                   | ✅           | ✅          |
| **SQLite Database**   | Internal    | ❌                   | ✅           | ✅          |
| **ContentProvider**   | Internal    | Opcional            | ✅           | ✅          |
| **KeyStore**          | System      | ✅                   | ❌           | ❌          |
| **EncryptedFile**     | Internal    | ✅                   | ✅           | ✅          |

### **Implementações Vulneráveis**

```java
// InsecureStorage.java - Exemplos de armazenamento inseguro

public class InsecureStorage {
    
    // 1. SharedPreferences inseguro
    public void insecureSharedPreferences(Context context) {
        SharedPreferences prefs = context.getSharedPreferences("user_data", MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        
        // Senha em texto plano
        editor.putString("password", "my_secret_password");
        editor.putString("token", "auth_token_123");
        editor.apply();
        
        // Dados sensíveis expostos
        String password = prefs.getString("password", "");
    }
    
    // 2. Arquivo interno inseguro
    public void insecureInternalFile(Context context) {
        String filename = "secret.txt";
        String data = "Sensitive: credit_card=4111111111111111";
        
        try {
            FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
            fos.write(data.getBytes());
            fos.close();
        } catch (IOException e) {}
    }
    
    // 3. Arquivo externo inseguro
    public void insecureExternalFile(Context context) {
        File sdCard = Environment.getExternalStorageDirectory();
        File file = new File(sdCard, "sensitive_data.txt");
        
        try {
            FileWriter writer = new FileWriter(file);
            writer.write("API_KEY=123456789");
            writer.close();
        } catch (IOException e) {}
    }
    
    // 4. SQLite Database inseguro
    public void insecureSQLite(Context context) {
        SQLiteDatabase db = context.openOrCreateDatabase("app.db", MODE_PRIVATE, null);
        
        // Criar tabela
        db.execSQL("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT, password TEXT)");
        
        // Inserir dados sensíveis
        db.execSQL("INSERT INTO users VALUES (1, 'admin', 'admin123')");
        db.execSQL("INSERT INTO users VALUES (2, 'user', 'password')");
        
        db.close();
    }
    
    // 5. Cache inseguro
    public void insecureCache(Context context) {
        File cacheDir = context.getCacheDir();
        File cacheFile = new File(cacheDir, "session_cache");
        
        try {
            FileOutputStream fos = new FileOutputStream(cacheFile);
            fos.write("session_token=xyz789".getBytes());
            fos.close();
        } catch (IOException e) {}
    }
}
```

### **Implementações Seguras**

```java
// SecureStorage.java - Exemplos de armazenamento seguro

import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;
import java.io.File;
import java.io.FileOutputStream;
import java.security.KeyStore;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;

public class SecureStorage {
    
    // 1. EncryptedSharedPreferences (AndroidX Security)
    public void secureSharedPreferences(Context context) {
        try {
            String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
            
            SharedPreferences prefs = EncryptedSharedPreferences.create(
                "secure_prefs",
                masterKeyAlias,
                context,
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
            );
            
            SharedPreferences.Editor editor = prefs.edit();
            editor.putString("password", "my_secret_password");
            editor.apply();
            
        } catch (Exception e) {}
    }
    
    // 2. Criptografia manual
    public void secureFileWithEncryption(Context context) {
        try {
            // Gerar chave AES
            KeyGenerator keyGen = KeyGenerator.getInstance("AES");
            keyGen.init(256);
            SecretKey secretKey = keyGen.generateKey();
            
            // Inicializar cipher
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            
            // Dados a serem criptografados
            byte[] plaintext = "sensitive_data".getBytes();
            byte[] ciphertext = cipher.doFinal(plaintext);
            
            // Salvar dados criptografados
            FileOutputStream fos = context.openFileOutput("secure.dat", Context.MODE_PRIVATE);
            fos.write(ciphertext);
            fos.close();
            
        } catch (Exception e) {}
    }
    
    // 3. Android KeyStore
    public void secureKeyStore(Context context) {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            
            // Gerar chave no KeyStore
            KeyGenerator keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
            
            keyGenerator.init(new KeyGenParameterSpec.Builder(
                "my_key",
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .build());
            
            SecretKey key = keyGenerator.generateKey();
            
        } catch (Exception e) {}
    }
}
```

***

## ⚔️ **Mecanismos de Ataque**

### **Fluxo de Ataque**

```mermaid
sequenceDiagram
    participant A as Atacante
    participant D as Dispositivo
    participant App as App Alvo

    Note over A,D: 1. Acesso Físico
    A->>D: Acesso ao dispositivo
    D->>D: Dispositivo com root/desbloqueado

    Note over A,App: 2. Extração de Dados
    A->>App: ADB backup
    App-->>A: Dados do app
    A->>App: Acesso a /data/data
    App-->>A: SharedPreferences/DB

    Note over A,App: 3. Análise
    A->>A: Lê dados em texto plano
    A->>A: Extrai credenciais
    A->>A: Compromete conta
```

### **Vetores de Ataque**

```yaml
Vetores Comuns:

  🔴 Dispositivo Rooteado:
    - Acesso total ao /data/data
    - Leitura de SharedPreferences
    - Leitura de SQLite databases
    - Extração de arquivos internos

  🔴 Backup ADB:
    - Backup sem senha
    - Extração de dados do app
    - Análise offline

  🔴 External Storage:
    - Arquivos em /sdcard/
    - Leitura por outros apps
    - Persistência de dados

  🔴 Cache:
    - Dados temporários persistentes
    - Imagens/vídeos sensíveis
    - Arquivos baixados

  🔴 Logs:
    - Logcat com dados sensíveis
    - Debug logs em produção
    - Stack traces expostos
```

***

## 🔧 **Técnicas de Exploração**

### **Técnica 1: Extração de SharedPreferences**

```python
#!/usr/bin/env python3
# extract_sharedprefs.py - Extração de SharedPreferences

import subprocess
import sys
import os

class SharedPreferencesExtractor:
    """Extração de SharedPreferences via ADB"""
    
    @staticmethod
    def extract_prefs(package):
        """Extrair SharedPreferences do pacote"""
        print(f"[*] Extraindo SharedPreferences de {package}")
        
        # Caminho das SharedPreferences
        prefs_path = f"/data/data/{package}/shared_prefs/"
        
        # Listar arquivos
        cmd = ['adb', 'shell', 'ls', prefs_path]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        files = result.stdout.strip().split('\n')
        
        for filename in files:
            if filename.endswith('.xml'):
                print(f"   Encontrado: {filename}")
                
                # Extrair arquivo
                pull_cmd = ['adb', 'pull', f"{prefs_path}/{filename}", "."]
                subprocess.run(pull_cmd, capture_output=True)
                
                # Analisar XML
                with open(filename, 'r') as f:
                    content = f.read()
                    print(f"   Conteúdo: {content[:200]}...")
    
    @staticmethod
    def parse_prefs_file(filename):
        """Parsear arquivo de SharedPreferences"""
        import xml.etree.ElementTree as ET
        
        try:
            tree = ET.parse(filename)
            root = tree.getroot()
            
            print("\n   Dados extraídos:")
            for elem in root:
                if elem.tag == 'string':
                    print(f"      String: {elem.get('name')} = {elem.text}")
                elif elem.tag == 'int':
                    print(f"      Int: {elem.get('name')} = {elem.get('value')}")
                elif elem.tag == 'boolean':
                    print(f"      Boolean: {elem.get('name')} = {elem.get('value')}")
                    
        except Exception as e:
            print(f"   Erro ao parsear: {e}")
    
    @staticmethod
    def exploit_scenarios():
        """Cenários de exploração"""
        print("\n[*] Cenários de exploração:")
        
        scenarios = [
            "Senhas armazenadas em texto plano",
            "Tokens de autenticação expostos",
            "Configurações sensíveis (API keys)",
            "Dados de usuário (nome, email, telefone)",
            "Preferências de pagamento"
        ]
        
        for scenario in scenarios:
            print(f"   • {scenario}")

# Uso
if __name__ == "__main__":
    print("💾 SharedPreferences Extraction")
    print("=" * 60)
    
    if len(sys.argv) > 1:
        SharedPreferencesExtractor.extract_prefs(sys.argv[1])
    else:
        print("Uso: extract_sharedprefs.py <package>")
    
    SharedPreferencesExtractor.exploit_scenarios()
```

### **Técnica 2: Extração de SQLite Database**

```python
#!/usr/bin/env python3
# extract_sqlite.py - Extração de SQLite Database

import subprocess
import sqlite3
import sys
import os

class SQLiteExtractor:
    """Extração e análise de SQLite databases"""
    
    @staticmethod
    def extract_database(package, db_name):
        """Extrair database do pacote"""
        print(f"[*] Extraindo database de {package}")
        
        db_path = f"/data/data/{package}/databases/{db_name}"
        
        # Verificar se database existe
        cmd = ['adb', 'shell', 'test', '-f', db_path, '&&', 'echo', 'exists']
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if 'exists' in result.stdout:
            print(f"   Database encontrada: {db_name}")
            
            # Extrair arquivo
            pull_cmd = ['adb', 'pull', db_path, f"{db_name}.db"]
            subprocess.run(pull_cmd, capture_output=True)
            
            return f"{db_name}.db"
        else:
            print(f"   Database não encontrada: {db_name}")
            return None
    
    @staticmethod
    def analyze_database(db_file):
        """Analisar conteúdo do database"""
        if not os.path.exists(db_file):
            return
        
        print(f"\n[*] Analisando {db_file}")
        
        try:
            conn = sqlite3.connect(db_file)
            cursor = conn.cursor()
            
            # Listar tabelas
            cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
            tables = cursor.fetchall()
            
            print(f"   Tabelas encontradas: {len(tables)}")
            
            for table in tables:
                table_name = table[0]
                print(f"\n   Tabela: {table_name}")
                
                # Obter colunas
                cursor.execute(f"PRAGMA table_info({table_name})")
                columns = cursor.fetchall()
                print(f"      Colunas: {[c[1] for c in columns]}")
                
                # Obter dados
                cursor.execute(f"SELECT * FROM {table_name}")
                rows = cursor.fetchall()
                
                print(f"      Registros: {len(rows)}")
                for row in rows[:5]:  # Mostrar apenas 5 primeiros
                    print(f"         {row}")
            
            conn.close()
            
        except Exception as e:
            print(f"   Erro: {e}")
    
    @staticmethod
    def search_sensitive_data(db_file, keywords):
        """Buscar dados sensíveis no database"""
        if not os.path.exists(db_file):
            return
        
        print(f"\n[*] Buscando dados sensíveis em {db_file}")
        
        try:
            conn = sqlite3.connect(db_file)
            cursor = conn.cursor()
            
            # Buscar em todas as tabelas
            cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
            tables = cursor.fetchall()
            
            for table in tables:
                table_name = table[0]
                
                for keyword in keywords:
                    try:
                        query = f"SELECT * FROM {table_name} WHERE"
                        conditions = []
                        
                        cursor.execute(f"PRAGMA table_info({table_name})")
                        columns = cursor.fetchall()
                        
                        for col in columns:
                            conditions.append(f"CAST({col[1]} AS TEXT) LIKE '%{keyword}%'")
                        
                        if conditions:
                            query += " OR ".join(conditions)
                            cursor.execute(query)
                            rows = cursor.fetchall()
                            
                            if rows:
                                print(f"   Encontrado em {table_name}: '{keyword}'")
                                for row in rows[:3]:
                                    print(f"      {row}")
                    except:
                        pass
            
            conn.close()
            
        except Exception as e:
            print(f"   Erro: {e}")

# Uso
if __name__ == "__main__":
    print("💾 SQLite Database Extraction")
    print("=" * 60)
    
    if len(sys.argv) > 2:
        extractor = SQLiteExtractor()
        db_file = extractor.extract_database(sys.argv[1], sys.argv[2])
        
        if db_file:
            extractor.analyze_database(db_file)
            extractor.search_sensitive_data(db_file, ['password', 'token', 'credit', 'ssn', 'email'])
    else:
        print("Uso: extract_sqlite.py <package> <database_name>")
```

### **Técnica 3: ADB Backup Extraction**

```bash
#!/bin/bash
# adb_backup_extract.sh - Extração de backup ADB

# 1. Criar backup do app
adb backup -f backup.ab -apk com.example.app

# 2. Verificar backup (requer senha se protegido)
file backup.ab

# 3. Extrair backup (usando dd + zlib)
dd if=backup.ab bs=1 skip=24 | python -c "import zlib,sys; sys.stdout.write(zlib.decompress(sys.stdin.read()))" > backup.tar

# 4. Extrair tar
tar -xvf backup.tar

# 5. Analisar arquivos extraídos
find . -name "*.xml" -exec cat {} \;
find . -name "*.db" -exec sqlite3 {} .tables \;

# 6. Extrair sem senha (usando abe)
java -jar abe.jar unpack backup.ab backup.tar
tar -xvf backup.tar
```

### **Técnica 4: Análise de Cache**

```python
#!/usr/bin/env python3
# cache_analyzer.py - Análise de cache

import subprocess
import os
import sys

class CacheAnalyzer:
    """Análise de cache de aplicações"""
    
    @staticmethod
    def extract_cache(package):
        """Extrair cache do aplicativo"""
        print(f"[*] Extraindo cache de {package}")
        
        cache_path = f"/data/data/{package}/cache/"
        
        # Listar arquivos de cache
        cmd = ['adb', 'shell', 'ls', '-la', cache_path]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        print(f"   Arquivos de cache:")
        for line in result.stdout.split('\n'):
            if line.strip() and not line.startswith('total'):
                print(f"      {line}")
    
    @staticmethod
    def extract_external_cache(package):
        """Extrair cache externo"""
        print(f"\n[*] Extraindo cache externo de {package}")
        
        ext_cache_path = f"/sdcard/Android/data/{package}/cache/"
        
        cmd = ['adb', 'shell', 'ls', '-la', ext_cache_path]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.stdout:
            print(f"   Arquivos de cache externo:")
            for line in result.stdout.split('\n'):
                if line.strip() and not line.startswith('total'):
                    print(f"      {line}")
        else:
            print("   Nenhum cache externo encontrado")
    
    @staticmethod
    def search_sensitive_cache(package, keywords):
        """Buscar dados sensíveis no cache"""
        print(f"\n[*] Buscando dados sensíveis no cache de {package}")
        
        cache_path = f"/data/data/{package}/cache/"
        
        for keyword in keywords:
            cmd = ['adb', 'shell', 'grep', '-r', keyword, cache_path]
            result = subprocess.run(cmd, capture_output=True, text=True)
            
            if result.stdout:
                print(f"   Encontrado '{keyword}':")
                for line in result.stdout.split('\n')[:3]:
                    print(f"      {line[:100]}")

# Uso
if __name__ == "__main__":
    print("💾 Cache Analysis")
    print("=" * 60)
    
    if len(sys.argv) > 1:
        CacheAnalyzer.extract_cache(sys.argv[1])
        CacheAnalyzer.extract_external_cache(sys.argv[1])
        CacheAnalyzer.search_sensitive_cache(sys.argv[1], ['password', 'token', 'session', 'credit'])
    else:
        print("Uso: cache_analyzer.py <package>")
```

### **Técnica 5: Frida Script para Hook de Storage**

```javascript
// frida_storage_hook.js - Hook de métodos de armazenamento

Java.perform(function() {
    console.log("[*] Hooking Storage Methods");
    
    // 1. Hook SharedPreferences
    var SharedPreferences = Java.use("android.app.SharedPreferencesImpl");
    
    SharedPreferences.getString.overload('java.lang.String', 'java.lang.String').implementation = function(key, defValue) {
        var value = this.getString(key, defValue);
        console.log("[SharedPreferences] GET: " + key + " = " + value);
        return value;
    };
    
    SharedPreferences.edit.implementation = function() {
        console.log("[SharedPreferences] EDIT called");
        return this.edit();
    };
    
    // 2. Hook SQLiteDatabase
    var SQLiteDatabase = Java.use("android.database.sqlite.SQLiteDatabase");
    
    SQLiteDatabase.execSQL.overload('java.lang.String').implementation = function(sql) {
        console.log("[SQLite] execSQL: " + sql);
        return this.execSQL(sql);
    };
    
    SQLiteDatabase.insert.overload('java.lang.String', 'java.lang.String', 'android.content.ContentValues').implementation = function(table, nullColumnHack, values) {
        console.log("[SQLite] INSERT INTO " + table);
        if (values != null) {
            var keySet = values.keySet();
            var iterator = keySet.iterator();
            while (iterator.hasNext()) {
                var key = iterator.next();
                var value = values.get(key);
                console.log("    " + key + " = " + value);
            }
        }
        return this.insert(table, nullColumnHack, values);
    };
    
    // 3. Hook FileOutputStream
    var FileOutputStream = Java.use("java.io.FileOutputStream");
    
    FileOutputStream.write.overload('[B').implementation = function(data) {
        var str = Java.use("java.lang.String").$new(data);
        console.log("[File] WRITE: " + str);
        return this.write(data);
    };
    
    // 4. Hook Cipher (criptografia)
    var Cipher = Java.use("javax.crypto.Cipher");
    
    Cipher.doFinal.overload('[B').implementation = function(input) {
        console.log("[Cipher] doFinal called");
        var result = this.doFinal(input);
        
        // Log dos dados (cuidado com dados grandes)
        if (input.length < 1024) {
            var str = Java.use("java.lang.String").$new(input);
            console.log("    Input: " + str);
        }
        
        return result;
    };
});
```

***

## 🔍 **Detecção e Monitoramento**

### **Script de Detecção de Storage Inseguro**

```python
#!/usr/bin/env python3
# detect_insecure_storage.py - Detector de storage inseguro

import subprocess
import re
import sys

class InsecureStorageDetector:
    """Detector de armazenamento inseguro"""
    
    @staticmethod
    def check_shared_prefs(package):
        """Verificar SharedPreferences inseguras"""
        print(f"[*] Verificando SharedPreferences de {package}")
        
        prefs_path = f"/data/data/{package}/shared_prefs/"
        
        cmd = ['adb', 'shell', 'ls', prefs_path]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        files = result.stdout.strip().split('\n')
        
        for filename in files:
            if filename.endswith('.xml'):
                # Verificar permissões
                perm_cmd = ['adb', 'shell', 'ls', '-l', f"{prefs_path}/{filename}"]
                perm_result = subprocess.run(perm_cmd, capture_output=True, text=True)
                
                if 'rw-rw-rw' in perm_result.stdout or 'rw-r--r--' in perm_result.stdout:
                    print(f"   ⚠️ SharedPreferences world-readable: {filename}")
    
    @staticmethod
    def check_external_storage(package):
        """Verificar uso de external storage"""
        print(f"\n[*] Verificando external storage de {package}")
        
        ext_path = f"/sdcard/Android/data/{package}/"
        
        cmd = ['adb', 'shell', 'ls', '-la', ext_path]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.stdout:
            print(f"   ⚠️ App usando external storage (mundo acessível)")
            for line in result.stdout.split('\n'):
                if 'rw-rw-rw' in line or 'rw-r--r--' in line:
                    print(f"      {line}")
    
    @staticmethod
    def check_logs(package):
        """Verificar logs do aplicativo"""
        print(f"\n[*] Verificando logs de {package}")
        
        cmd = ['adb', 'logcat', '-d', '-s', package]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        sensitive_patterns = [
            'password', 'token', 'credit', 'ssn', 'secret',
            'key', 'auth', 'api_key', 'private', 'confidential'
        ]
        
        for line in result.stdout.split('\n'):
            line_lower = line.lower()
            for pattern in sensitive_patterns:
                if pattern in line_lower:
                    print(f"   ⚠️ Dado sensível em log: {line[:100]}")
                    break
    
    @staticmethod
    def check_backup_flag(package):
        """Verificar flag de backup"""
        print(f"\n[*] Verificando flag de backup de {package}")
        
        cmd = ['adb', 'shell', 'dumpsys', 'package', package]
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if 'allowBackup=true' in result.stdout:
            print("   ⚠️ App permite backup (dados podem ser extraídos via ADB)")
        
        if 'fullBackupOnly=true' in result.stdout:
            print("   ⚠️ App permite full backup")

# Uso
if __name__ == "__main__":
    print("🔍 Insecure Data Storage Detection")
    print("=" * 60)
    
    if len(sys.argv) > 1:
        InsecureStorageDetector.check_shared_prefs(sys.argv[1])
        InsecureStorageDetector.check_external_storage(sys.argv[1])
        InsecureStorageDetector.check_logs(sys.argv[1])
        InsecureStorageDetector.check_backup_flag(sys.argv[1])
    else:
        print("Uso: detect_insecure_storage.py <package>")
```

***

## 🛡️ **Mitigações e Hardening**

### **Implementação Segura**

```java
// SecureDataStorage.java - Implementação segura

import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;
import java.io.File;
import java.io.FileOutputStream;
import java.security.KeyStore;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;

public class SecureDataStorage {
    
    // 1. Configuração segura no Manifest
    // <application
    //     android:allowBackup="false"
    //     android:fullBackupContent="false">
    
    // 2. SharedPreferences segura
    public void securePreferences(Context context) {
        try {
            String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
            
            SharedPreferences prefs = EncryptedSharedPreferences.create(
                "secure_prefs",
                masterKeyAlias,
                context,
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
            );
            
            // Armazenar dados sensíveis
            prefs.edit()
                .putString("api_key", "encrypted_key")
                .putString("user_token", "encrypted_token")
                .apply();
                
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 3. Arquivo criptografado
    public void secureFile(Context context) {
        try {
            File file = new File(context.getFilesDir(), "secure.dat");
            
            // Usar EncryptedFile (AndroidX Security)
            String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
            
            EncryptedFile encryptedFile = new EncryptedFile.Builder(
                file,
                context,
                masterKeyAlias,
                EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
            ).build();
            
            FileOutputStream fos = encryptedFile.openFileOutput();
            fos.write("sensitive_data".getBytes());
            fos.close();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 4. Android KeyStore para chaves
    public void secureKeyStore() {
        try {
            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
            keyStore.load(null);
            
            KeyGenerator keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
            
            keyGenerator.init(new KeyGenParameterSpec.Builder(
                "my_key_alias",
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setUserAuthenticationRequired(true)  // Requer autenticação do usuário
                .build());
            
            SecretKey key = keyGenerator.generateKey();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 5. Evitar logs em produção
    public void secureLogging(String message) {
        if (BuildConfig.DEBUG) {
            Log.d("App", message);
        }
        // Em produção, não logar dados sensíveis
    }
}
```

### **Configuração no Manifest**

```xml
<!-- AndroidManifest.xml - Configuração segura -->
<application
    android:allowBackup="false"
    android:fullBackupContent="false"
    android:usesCleartextTraffic="false">
    
    <!-- Evitar debug mode em produção -->
    <meta-data
        android:name="android.security.KEYSTORE"
        android:value="AndroidKeyStore" />
        
</application>
```

### **ProGuard Rules**

```proguard
# proguard-rules.pro - Ofuscação para proteger dados

# Ofuscar nomes de classes
-keep class com.example.app.data.** { *; }

# Remover logs
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
    public static *** w(...);
    public static *** e(...);
}

# Ofuscar strings
-obfuscationdictionary dictionary.txt
-classobfuscationdictionary dictionary.txt
-packageobfuscationdictionary dictionary.txt
```

***

## 📋 **Checklists de Segurança**

### **Checklist para Desenvolvedores**

* [ ] Não armazenar dados sensíveis em texto plano
* [ ] Usar EncryptedSharedPreferences
* [ ] Configurar android:allowBackup="false"
* [ ] Evitar external storage para dados sensíveis
* [ ] Limpar cache após uso
* [ ] Não logar dados sensíveis
* [ ] Usar Android KeyStore para chaves

### **Checklist para Pentesters**

* [ ] Extrair SharedPreferences
* [ ] Extrair SQLite databases
* [ ] Verificar backup ADB
* [ ] Analisar cache
* [ ] Verificar logs
* [ ] Testar external storage

***

## 📊 **Conclusão**

```yaml
Insecure Data Storage:

  🔴 Principais Vetores:
    - SharedPreferences em texto plano
    - SQLite sem criptografia
    - External storage acessível
    - Cache persistente
    - Logs com dados sensíveis

  🛡️ Mitigações Essenciais:
    - Criptografia de dados
    - EncryptedSharedPreferences
    - android:allowBackup="false"
    - Android KeyStore
    - Logs apenas em debug

  🎯 Prioridade:
    - CRÍTICA: Dados de autenticação
    - ALTA: Dados pessoais
    - MÉDIA: Dados de configuração
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xmorte.gitbook.io/bibliadopentestbr/tecnicas/sistemas-operacionais/android/insecure-data-storage.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
