Salta al contenuto principale
Mobile Sicurezza

Best Practices per lo Storage dei Token nelle App Mobile (2026)

Best practice per lo storage dei token: access token in memoria, refresh token in Keychain o Keystore, rotazione e abbandono di EncryptedSharedPreferences.

Come archiviare in modo sicuro access e refresh token su mobile con Keychain e Keystore

Il modo più sicuro per archiviare gli access token in un'app moderna è non persisterli: tenere l'access token di breve durata in memoria durante la sessione e generarne uno nuovo a ogni avvio da un refresh token salvato in uno storage cifrato gestito dalla piattaforma. Persistere solo il refresh token su iOS nel Keychain e su Android con Android Keystore (software, TEE o StrongBox a seconda del dispositivo) più Jetpack DataStore + Tink. EncryptedSharedPreferences è deprecato nella androidx.security.crypto; pianifica le migrazioni ora. Per Capacitor le app, combina la sicurezza del trasporto (HTTPS, strumenti e plugin per SSL pinning) con plugin di storage nativo e @capgo/capacitor-native-biometric invece di duplicare la crittografia in JavaScript.

Come abbiamo scelto questi pattern di storage

Ogni raccomandazione qui passa quattro filtri. Primo, archiviazione protetta dalla piattaforma: i segreti devono risiedere in Keychain o Keystore, oppure essere cifrati con chiavi conservate lì. Preferisci l'isolamento basato sull'hardware quando il dispositivo e la configurazione delle chiavi lo supportano effettivamente (ad esempio chiavi basate su StrongBox o Secure Enclave). Secondo, un percorso di migrazione ufficialmente supportato. Se Google o Apple hanno deprecato l'approccio, non lo raccomandiamo per nuovi code. Terzo, compatibilità con Capacitor: il pattern deve funzionare da un confine di plugin senza esporre chiavi grezze a JavaScript. Quarto, revocabilità: una credenziale compromessa deve poter essere invalidata lato server.

L'ultimo filtro è la ragione per cui la rotazione dei refresh token compare in tutto l'articolo. Chainalysis ha riportato oltre 305 milioni di dollari rubati a DMM Bitcoin in Giappone dopo la compromissione di una chiave privata nel 2024 (Rapporto Chainalysis 2024 sulla criminalità). Anche i token di auth mobile falliscono allo stesso modo: un bearer token a lunga durata nello storage in chiaro è un invito aperto. Smetteremmo di consigliare qualsiasi architettura che mantenga indefinitamente una credenziale senza rotazione.

Principi applicabili a ogni piattaforma

La tabella seguente comprime in snapshot; la logica sottostante è la separazione access/refresh token. Un token di accesso è di breve durata, minuti non giorni, ed è quello che il tuo API considera presente. Poiché il suo raggio d'azione è limitato e muore in fretta, va tenuto solo in memoria, ricostruito a ogni avvio dell'app. Il refresh token è il segreto durevole che ti permette di generare nuovi access token, quindi solo lui merita un posto in uno storage persistente e crittografato con supporto hardware. Durate diverse giustificano sedi diverse; conservarli allo stesso modo è il punto di partenza della maggior parte delle fughe di dati.

La sequenza completa a runtime è questa: l'app si avvia senza access token, chiama il tuo server di autenticazione con il refresh token persistito, riceve un nuovo access token in memoria e distrugge il vecchio refresh token perché il server lo ruota a ogni utilizzo. Se il server vede ripresentare un refresh token già consumato, lo tratta come prova di furto e revoca l'intera famiglia di token. Al logout, il client cancella lo storage locale e il server revoca il grant. Eliminare i dati localmente senza revoca dal server è speranza, non sicurezza.

Pratica Perché è importante
Access token di breve durata Limita i danni in caso di token compromesso
Rotazione del refresh token Invalida i refresh token rubati dopo un utilizzo legittimo
Solo HTTPS Impedisce l'intercettazione casuale della rete
Certificate pinning (quando giustificato) Riduce il rischio di root mal configurate o personalizzate; consulta le linee guida OWASP sul certificate pinning
Nessun token nei log, negli URL o negli analytics I token negli strumenti di osservabilità diventano violazioni
Revoca al logout L'invalidazione lato server batte la speranza che il client elimini i dati

@capgo/capacitor-ssl-pinning può fare pinning TLS per Capacitor HTTP su iOS e Android quando servono controlli di trasporto più rigorosi. Per una panoramica più ampia sull'hardening del trasporto, vedi Strumenti e plugin per implementare l'SSL pinning.

Android: Keystore e DataStore (non EncryptedSharedPreferences)

Google ha deprecato il androidx.security.crypto helper, tra cui EncryptedSharedPreferences e MasterKey. Il riepilogo del pacchetto indica agli sviluppatori di usare KeyGenerator con il provider AndroidKeyStore per il nuovo materiale delle chiavi.

Usa una pipeline basata su Tink end-to-end:

  1. Crea un keyset Tink con AndroidKeysetManager, salvando la master key in Android Keystore tramite un android-keystore:// URI (vedi le datastore-tink note di rilascio). Call isUsingKeystore() sul manager e fallire in modo sicuro quando restituisce false. Altrimenti Tink può archiviare il keyset in chiaro.
  2. Ottieni un Aead primitiva da quel keyset.
  3. Aggiungi androidx.datastore:datastore-tink (DataStore 1.3.0 fornisce l'artifact; la 1.2.x non lo fa), incapsula il tuo Serializer con AeadSerializer, e passalo a DataStore. Quando più store condividono la stessa Aead chiave, assegna a ogni store una associatedDataunica, come un valore stabile derivato dal nome del file.

Google lo documenta come il percorso di sostituzione supportato per gli androidx.security.crypto helpers. Non mischiare un JCA standalone SecretKey con AeadSerializer; il serializer si aspetta un Tink Aead instance.

Opzioni Keystore da conoscere

  • setIsStrongBoxBacked(true) quando FEATURE_STRONGBOX_KEYSTORE è disponibile per un isolamento più forte delle chiavi.
  • Autenticazione utente binding (setUserAuthenticationRequired) quando i token devono essere accessibili solo dopo biometria o credenziali del dispositivo.
  • Evita Auto Backup per file che contengono keyset o ciphertext, a meno che tu non comprenda il comportamento di restore; la documentazione di EncryptedSharedPreferences di Google avvertiva che il backup può ripristinare il ciphertext senza le chiavi.

I moduli nativi Kotlin o Java in Capacitor dovrebbero implementare questo pattern all'interno del plugin; JavaScript dovrebbe ricevere solo handle opachi o lo stato della sessione, non le chiavi grezze.

iOS: Keychain e Secure Enclave

Il Keychain rimane il posto corretto per i token su iOS. Gli elementi sono cifrati e protetti dal sistema. Usa una costante di accessibilità con ambito ristretto e Controllo degli accessi quando ti serve la protezione con Face ID, Touch ID o passcode.

La scelta dell'accessibilità è una decisione reale, non un valore predefinito da copiare. Scegli kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly quando i token devono essere utilizzabili da attività in background, come refresh attivati da push o background fetch, ma non devono mai migrare su un nuovo dispositivo tramite backup. Scegli kSecAttrAccessibleWhenUnlockedThisDeviceOnly per l'uso dei token solo in foreground, dove una finestra più restrittiva vale il vincolo. In entrambi i casi, abilita kSecUseDataProtectionKeychain sui target moderni per ottenere il Keychain con data-protection anziché quello legacy basato su file.

CryptoKit è utile quando crittografi tu stesso i payload, ma per i token bearer standard in stile OAuth, memorizzare la stringa del token direttamente in Keychain con la corretta accessibilità di solito basta. Le chiavi supportate da Secure Enclave (una chiave P-256 generata con SecureEnclave in CryptoKit) hanno senso solo se la chiave stessa non deve mai lasciare l'hardware sicuro, ad esempio per operazioni di firma o attestazione del dispositivo. Non possono conservare una stringa di token al posto tuo; proteggono la tua chiave di envelope. Se non ruoti gli envelope e non ne gestisci il ciclo di vita, la semplice archiviazione in Keychain è più semplice e non meno sicura. La crittografia aggiuntiva a livello di app aiuta solo se ruoti anche le chiavi e gestisci gli envelope con attenzione.

Capacitor @capacitor/preferences scrive in UserDefaults, che non è hai memorizzazione segreta. Non metterci i token.

Capacitor: opzioni di storage sicuro nell'org Cap-go

Cap-go fa hai rilasciare un pacchetto chiamato “secure storage”, ma due plugin coprono le esigenze comuni di Capacitor:

Plugin Ideale per Supporto nativo
@capgo/capacitor-native-biometric setData / getSecureData Piccoli segreti (token di sessione, chiavi API) iOS Keychain; crittografia basata su Android Keystore in SharedPreferences
@capgo/capacitor-data-storage-sqlite con opzioni di crittografia Dati locali più grandi o strutturati SQLite con SQLCipher opzionale

Il guida allo storage key-value confronta Preferences, gli store SQLite e NativeBiometric.setData per i segreti.

Richiede @capgo/capacitor-native-biometric 8.6.0 o versioni successive (allinea la versione maggiore del plugin alla versione maggiore di Capacitor). Esempio con letture protette da biometria:

import {
  AccessControl,
  NativeBiometric,
} from '@capgo/capacitor-native-biometric';

const TOKEN_KEY = 'auth.refresh_token';

export async function saveRefreshToken(token: string) {
  await NativeBiometric.setData({
    key: TOKEN_KEY,
    value: token,
    accessControl: AccessControl.BIOMETRY_CURRENT_SET,
  });
}

export async function loadRefreshToken(): Promise<string | null> {
  try {
    const { value } = await NativeBiometric.getSecureData({
      key: TOKEN_KEY,
      reason: 'Unlock your session',
      title: 'Authenticate',
    });
    return value ?? null;
  } catch {
    return null;
  }
}

getData restituisce solo i valori memorizzati senza accessControl. Per la protezione biometrica, abbina sempre BIOMETRY_CURRENT_SET oppure BIOMETRY_ANY con getSecureData.

Per i build web, non persistere mai i token di produzione in localStorage. Usa invece il modello di sessione web del tuo backend.

Ciclo di vita dei token su server e client

Sì, le app mobili dovrebbero archiviare i refresh token. L'alternativa è costringere gli utenti a ri-autenticarsi ad ogni avvio, il che è una sicurezza di facciata peggiore della sicurezza reale. La sfumatura sta in dove e come: persistere solo il refresh token, solo in storage supportato da Keychain o Keystore, e solo con rotazione e revoca lato server.

Come gestire e ruotare in modo sicuro i refresh token in un'app mobile

La rotazione è ciò che rende la persistenza sicura. Emetti i refresh token come one-time-use : ogni riscatto restituisce un nuovo refresh token e invalida quello precedente. Il server mantiene una breve finestra di rilevamento del riuso. Se un token presumibilmente consumato viene presentato di nuovo, il server considera la famiglia compromessa e revoca l'intera catena, forzando un nuovo login su ogni dispositivo che lo condivideva. Consenti un breve periodo di grazia (da pochi secondi a un minuto) per richieste in competizione e riconnessioni offline, dove un client legittimo potrebbe presentare il vecchio token un'ultima volta prima di conoscere la sostituzione. Nessun periodo di grazia blocca gli utenti su connessioni instabili; una finestra troppo lunga allarga la superficie di attacco.

Dove risiede ciascun token dipende direttamente dalla sua durata:

  • Problema access token con TTL brevi (minuti, non giorni) e tienili solo in memoria durante la sessione.
  • Persisti il refresh token in storage cifrato basato su Keychain o Keystore, e ruotalo come sopra.
  • In caso di cambio password, logout o perdita del dispositivo, revoca i refresh token lato server. Eliminare i dati locali dal client non equivale a una revoca.
  • Valuta una strategia di token binding leggera (device key attestation o app instance ID) se il tuo threat model lo richiede.

Quando rilasci fix di sicurezza sulla gestione dei token nelle app Capacitor, Live updates di Capgo può distribuire rapidamente modifiche al livello web ammissibili, a patto di rispettare le policy degli store. Le modifiche allo storage nativo richiedono comunque un build dello store. Capgo’s panoramica sulla sicurezza riassume come le app in produzione combinano controlli di trasporto, storage e aggiornamenti.

Opzioni di storage a confronto

Metodo Sicurezza Compatibilità con Capacitor
Variabile JS in memoria Alta durante l'esecuzione, persa al kill OK solo per l'access token durante la sessione
localStorage / Preferenze Basso Solo impostazioni, mai segreti
Keychain iOS / Keystore Android Alta Tramite code nativo o plugin Capgo
DataStore + Tink (nativo Android) Alta Plugin Capacitor custom o SDK nativo per l'auth
Crittografia custom in JS Variabile Evita: le chiavi in JS sono estraibili

Da notare sull'ultima riga: il punto di rottura della crypto in JavaScript non sono gli algoritmi deboli, ma dove sta la chiave. Qualsiasi chiave che un runtime JS può leggere può essere esfiltrata da qualunque altra cosa giri nel runtime, quindi l'"alta sicurezza" è strutturalmente irraggiungibile da quel livello, qualunque libreria tu scelga.

Checklist di migrazione da EncryptedSharedPreferences

  1. Inventaria quali chiavi contengono segreti e quali config pubblica.
  2. Implementa DataStore + AeadSerializer con un keyset Tink protetto da Keystore.
  3. Migra con SharedPreferencesMigration usando un producer che apre la vecchia EncryptedSharedPreferences istanza (non quella plain SharedPreferences), poi elimina i file legacy dopo una lettura riuscita.
  4. Rilascia la modifica nativa via store; testa backup e restore su un dispositivo fisico.
  5. Rimuovi androidx.security.crypto dipendenze quando nessuno le referenzia.

FAQ

Come si implementa lo storage sicuro dei token?

Su iOS, scrivi i refresh token nel Keychain con un livello di accessibilità adeguato alle esigenze di refresh in background. Su Android, usa un keyset Tink in Android Keystore con Jetpack DataStore e AeadSerializer al posto delle deprecate EncryptedSharedPreferences. Tieni gli access token solo in memoria e usa HTTPS. Aggiungi certificate pinning quando il tuo threat model lo richiede.

Come gestisco e ruoto in modo sicuro i refresh token in un'app mobile?

Conserva il refresh token solo in storage supportato da Keychain o Keystore. Emetti refresh token monouso in modo che ogni utilizzo restituisca un nuovo refresh token e invalidi quello precedente. Mantieni una breve finestra di rilevamento del riutilizzo, così un token riprodotto revoca l'intera famiglia, con un piccolo periodo di grazia per client offline o in race condition.

Lo storage sicuro di access token e refresh token è lo stesso su iOS e Android?

La suddivisione è la stessa su entrambe le piattaforme: memoria per access token a breve durata, storage cifrato protetto dalla piattaforma per refresh token. Le API differiscono (Keychain contro Keystore più DataStore), ma entrambe devono evitare WebView localStorage, UserDefaults e SharedPreferences non cifrati per i segreti.

Dovrei crittografare i token prima di salvarli nel Keychain?

Di solito non serve un livello extra per i bearer token standard se l'accessibilità di Keychain corrisponde al tuo threat model. Aggiungi cifratura a envelope quando le normative o le policy di rotazione delle chiavi lo richiedono.

SQLCipher in un plugin Capacitor sostituisce Keystore?

SQLCipher protegge il file del database, ma hai comunque bisogno di una chiave forte in Keystore o Keychain per decifrare il database. Tratta la gestione della passphrase di cifratura come parte del tuo design di storage sicuro.

Posso salvare i token in Capacitor Preferences con offuscamento?

L'obfuscation non è cifratura. Preferences mappa su UserDefaults e SharedPreferences senza protezione hardware-backed.

Le app mobile dovrebbero salvare i refresh token?

Sì, ma solo in storage cifrato basato su Keychain o Keystore, abbinato a rotazione e revoca lato server. Non salvarli significa ri-autenticarsi a ogni avvio, cosa che gli utenti aggirano richiedendo requisiti di login più deboli. Ciò che non è mai accettabile è il refresh token in Preferences, localStorage o in qualsiasi file in chiaro.

Qual è il modo più sicuro per salvare gli access token in un'app moderna?

Non salvarli affatto. Mantieni il token di accesso a breve scadenza in memoria durante la sessione e rigeneralo dal refresh token a ogni avvio. Persisti solo il refresh token in uno storage cifrato supportato da iOS Keychain o Android Keystore, e affidati a rotazione e revoca per limitare i danni in caso di compromissione del dispositivo.

Continua da Secure Token Storage: Best Practices for Mobile Developers

Se stai usando Storage sicuro dei token: best practice per sviluppatori mobile per pianificare sicurezza e compliance, collegalo a Crittografia per i dettagli di implementazione della crittografia, Conformità per i dettagli di implementazione in Compliance, Security Scanner di Capgo per il workflow del prodotto in Capgo Security Scanner, Sicurezza Capgo per il workflow del prodotto in Capgo Security, e Trust Center di Capgo per il workflow del prodotto in Capgo Trust Center.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.