Secure Storage è un insieme di metodi e tecnologie per proteggere i dati riservati su un dispositivo: token, chiavi di crittografia, informazioni di pagamento e dati personali degli utenti. Secondo OWASP Mobile Top 10 (2024), l'archiviazione non sicura dei dati è tra i tre rischi più critici. Una corretta implementazione di Secure Storage previene le fughe di dati anche con accesso fisico al dispositivo.
Punti chiave
Secure Storage è la pratica di archiviare i dati riservati di un'applicazione mobile in modo che siano inaccessibili ad altre applicazioni, malware e aggressori con accesso fisico al dispositivo. A differenza dell'archiviazione normale, Secure Storage utilizza crittografia, isolamento e protezione hardware.
Non tutti i dati richiedono Secure Storage: le immagini del profilo o la cache delle notizie possono essere archiviate nel filesystem normale. Tuttavia, le chiavi di crittografia, i token di autenticazione, i dati di pagamento, le chiavi private e i modelli biometrici devono essere protetti. Secondo Google Security Blog (2025), il 67% delle vulnerabilità nelle applicazioni mobili sono legate all'archiviazione di segreti in testo semplice.
Ogni piattaforma mobile fornisce i propri meccanismi Secure Storage: Android — Keystore e EncryptedSharedPreferences, iOS — Keychain e Data Protection API. Questi meccanismi sono integrati con moduli di sicurezza hardware (TEE, Secure Enclave) e garantiscono che i dati non possano essere letti nemmeno dopo il jailbreak o il root del dispositivo.
La scelta corretta del metodo Secure Storage dipende dal tipo di dati, dallo scenario di utilizzo e dai requisiti di prestazione. Comprendere l'architettura di ciascun meccanismo consente allo sviluppatore di prendere la giusta decisione architetturale.
La piattaforma Android offre diversi livelli di protezione dei dati, dalla memorizzazione hardware delle chiavi alle SharedPreferences crittografate. La scelta dipende dalla sensibilità dei dati e dai requisiti di prestazione.
Android Keystore è un fornitore crittografico che genera e memorizza le chiavi in un ambiente di esecuzione isolato (TEE — Trusted Execution Environment) sui dispositivi con supporto alla protezione hardware. Le chiavi non lasciano mai il TEE: le operazioni crittografiche vengono eseguite all'interno di un'area protetta inaccessibile anche al sistema operativo.
A partire da Android 9 (API 28), Keystore supporta StrongBox Keymaster — un chip di sicurezza dedicato con propria CPU, generatore di numeri casuali hardware (TRNG) e memoria protetta. StrongBox è certificato Common Criteria EAL 4+ ed è il più alto livello di sicurezza di archiviazione delle chiavi su Android. Per utilizzare StrongBox, è necessario specificare esplicitamente il flag inStrongBox() durante la generazione della chiave.
Keystore supporta gli algoritmi: AES/GCM/NoPadding (256 bit), EC (secp256r1, secp384r1), RSA (2048–4096 bit) e HMAC-SHA256. Tutte le chiavi possono essere legate all'autenticazione biometrica tramite setUserAuthenticationRequired(true).
EncryptedSharedPreferences è una libreria del pacchetto AndroidX Security che crittografa automaticamente tutti i dati salvati tramite l'API SharedPreferences. I valori vengono crittografati con una chiave AES-256 GCM e le chiavi vengono crittografate con AES-256 SIV (IV sintetico), prevenendo attacchi di dizionario sui nomi delle chiavi.
La chiave di crittografia principale è memorizzata in Android Keystore, fornendo una protezione a due livelli: Keystore protegge la chiave master, EncryptedSharedPreferences protegge i dati. Le prestazioni di crittografia sono inferiori a 5 ms per operazione di lettura/scrittura per dati tipici (token, impostazioni), rendendo la libreria adatta agli scenari utente.
EncryptedSharedPreferences non è progettato per grandi volumi di dati (oltre 5 MB) — per questi, utilizzare un database crittografato tramite SQLCipher o Room con crittografia.
SQLCipher è un'estensione di SQLite che crittografa l'intero database pagina per pagina con AES-256-CBC. Ogni pagina del database viene crittografata con una chiave separata derivata dalla password master tramite PBKDF2. SQLCipher aggiunge circa il 5–15% di overhead prestazionale a seconda della dimensione dei dati.
L'integrazione con Android avviene tramite la libreria net.zetetic:android-database-sqlcipher, che fornisce un'API compatibile con SQLiteOpenHelper standard. La password per SQLCipher dovrebbe essere memorizzata in Keystore, non nel codice o in SharedPreferences.
La piattaforma iOS fornisce Keychain Services come archivio sicuro principale e l'API Data Protection per la crittografia dei file a livello di sistema operativo.
Keychain è un database SQLite crittografato in cui iOS memorizza password, chiavi di crittografia, certificati e note. Ogni elemento Keychain (SecItem) viene memorizzato in forma crittografata utilizzando una chiave hardware unica del dispositivo. L'accesso a un elemento è controllato tramite una ACL (Access Control List), che può richiedere l'autenticazione biometrica (Face ID, Touch ID) o un codice di accesso.
Keychain supporta classi di protezione che determinano quando i dati sono accessibili: kSecAttrAccessibleWhenUnlockedThisDeviceOnly — i dati sono accessibili solo quando il dispositivo è sbloccato e non vengono trasferiti durante il backup. Questa classe è raccomandata per la maggior parte degli scenari di archiviazione di token di autenticazione.
Su iOS 15+, il Security framework è disponibile con supporto per chiavi hardware tramite Secure Enclave — un processore Apple dedicato che gestisce le operazioni crittografiche e memorizza le chiavi private in memoria isolata. Secure Enclave supporta gli algoritmi ECDSA (secp256r1) e ECDH per generare chiavi che non possono essere estratte dal chip.
Data Protection è un meccanismo iOS che crittografa ogni file a livello di filesystem (APFS) utilizzando una chiave legata al codice di accesso del dispositivo. Lo sviluppatore specifica il livello di protezione tramite l'attributo NSFileProtectionType durante la creazione del file: NSFileProtectionComplete — il file è accessibile solo quando il dispositivo è sbloccato.
Data Protection funziona automaticamente su tutti i dispositivi con iOS 5+ se è impostato un codice di accesso. La crittografia viene eseguita a livello hardware tramite il Dedicated AES Engine del processore Apple, garantendo prestazioni elevate — la latenza di crittografia è praticamente impercettibile per l'utente. Per attivare la protezione in un'applicazione, è sufficiente impostare l'attributo di protezione durante la creazione del file tramite FileManager.
Data Protection non sostituisce Keychain per la memorizzazione delle chiavi — viene utilizzato per crittografare file, database Core Data e altri grandi volumi di dati. La combinazione di Keychain (per le chiavi) e Data Protection (per i file) fornisce un ciclo completo di archiviazione sicura su iOS.
Vediamo esempi pratici di Secure Storage utilizzando le API integrate di Android e iOS.
L'esempio mostra l'inizializzazione di EncryptedSharedPreferences con una chiave master da Android Keystore. Tutte le successive operazioni di lettura e scrittura vengono automaticamente crittografate e decrittografate.
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val prefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
prefs.edit()
.putString("auth_token", "eyJhbGciOiJIUzI1NiJ9...")
.apply()
L'esempio mostra il salvataggio e la lettura dei dati da iOS Keychain utilizzando Security framework. Il codice utilizza kSecAttrAccessibleWhenUnlockedThisDeviceOnly per la massima protezione.
import Security
func saveToKeychain(key: String, data: Data) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
SecItemAdd(query as CFDictionary, nil)
}
func readFromKeychain(key: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(
query as CFDictionary, &result
)
return status == errSecSuccess ? result as? Data : nil
}
Un esempio di connessione a un database SQLite crittografato tramite SQLCipher con password memorizzata in Android Keystore.
import net.sqlcipher.database.SQLiteDatabase
import net.sqlcipher.database.SQLiteOpenHelper
class SecureDBHelper(context: Context) :
SQLiteOpenHelper(context, "secure.db", null, 1) {
private val password = getKeyFromKeystore()
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE tokens (id INTEGER PRIMARY KEY, value TEXT)")
}
override fun onUpgrade(
db: SQLiteDatabase, oldVersion: Int, newVersion: Int
) {
onCreate(db)
}
}
// Utilizzo: passiamo la password all'apertura
val helper = SecureDBHelper(context)
val db = helper.getWritableDatabase(password)
L'uso corretto di Secure Storage richiede di seguire diversi principi fondamentali che prevengono gli errori comuni degli sviluppatori.
Definire la classificazione dei dati: quali dati richiedono protezione hardware (Keystore / Secure Enclave), quali richiedono crittografia a livello di sistema operativo (EncryptedSharedPreferences / Data Protection) e quali possono essere archiviati nel filesystem normale. Token di autenticazione, chiavi private e dati di pagamento — solo livello hardware. Impostazioni utente (tema, lingua) — EncryptedSharedPreferences è sufficiente. Dati di sessione (cache temporanee) possono essere archiviati in memoria o in una directory temporanea.
Non memorizzare mai segreti nel codice: stringhe con chiavi API, password o frasi seed nel codice sorgente sono un grave errore di sicurezza. Qualsiasi reverse engineering esporrà immediatamente questi dati. Utilizzare Keystore per le chiavi e per la configurazione — caricamento lato server all'avvio dell'applicazione (config remota).
Utilizzare il binding biometrico per le operazioni critiche: Android Keystore e iOS Keychain supportano il legame delle chiavi all'autenticazione biometrica. Ogni volta che si accede a una chiave, il sistema richiede Face ID, Touch ID o la biometria Android (BiometricPrompt). Ciò garantisce che anche con il controllo totale del dispositivo, un aggressore non possa utilizzare i dati memorizzati senza il proprietario.
Testare la sicurezza: utilizzare strumenti di analisi della sicurezza — MobSF (Mobile Security Framework) per l'analisi statica, objection per i test runtime e Frida per bypassare la protezione. Verificare che i dati siano inaccessibili dopo il root o il jailbreak. Android consente di verificare l'accesso root tramite SafetyNet Attestation o Play Integrity API, iOS — tramite verifica dell'integrità di Secure Enclave.
Aggiornare regolarmente le librerie crittografiche: le vulnerabilità nelle librerie di crittografia vengono scoperte regolarmente. Monitorare i CVE per AndroidX Security, SQLCipher e wrapper Keychain. Implementare un sistema di notifica automatica per le nuove versioni tramite Dependabot o Renovate.
Secondo Apple Security Research (2025), una corretta implementazione di Secure Storage previene il 96% degli attacchi mirati al furto di dati dal dispositivo. Il restante 4% sono attacchi con accesso fisico e exploit zero-day, contro i quali il binding biometrico è efficace.
Domande frequenti
iOS Keychain è un database crittografato per memorizzare password, chiavi e certificati con controllo di accesso tramite ACL. Android Keystore è un fornitore crittografico che genera e memorizza chiavi in un ambiente isolato (TEE/StrongBox) e non consente di estrarre la chiave privata.
EncryptedSharedPreferences utilizza AES-256 GCM per crittografare i valori e AES-256 SIV per crittografare le chiavi. La chiave master è memorizzata in Android Keystore, fornendo una protezione a due livelli. Inoltre, HMAC-SHA256 viene utilizzato per la verifica dell'integrità.
Sì, HTTPS protegge i dati solo nel canale di trasmissione. Sul dispositivo, i dati vengono archiviati in testo semplice dopo la decrittografia. Se un aggressore ottiene accesso fisico al dispositivo o installa malware, HTTPS non proteggerà i dati archiviati. Crittografare sempre i dati a livello di archiviazione.
Utilizzare Android Keystore con il flag setUnlockedDeviceRequired(true), che blocca l'accesso alle chiavi sui dispositivi rooted. Inoltre, verificare l'integrità tramite Play Integrity API e, se si discosta dai valori di riferimento, cancellare tutti i segreti dall'archivio.
No, UserDefaults memorizza i dati in testo semplice in un file plist all'interno della sandbox. Qualsiasi applicazione con strumenti di reverse engineering (tramite backup o jailbreak) può leggere i token. Solo Keychain è il luogo sicuro per memorizzare segreti su iOS.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche