Elenco di controllo per la sicurezza

Android dispone di funzionalità di sicurezza integrate che riducono notevolmente la frequenza e l'impatto dei problemi di sicurezza delle applicazioni. Il sistema è progettato in modo che in genere tu possa creare le tue app con le autorizzazioni di sistema e dei file predefinite ed evitare decisioni difficili in materia di sicurezza.

Le seguenti funzionalità di sicurezza di base ti aiutano a creare app sicure:

  • La sandbox delle app Android, che isola i dati e l'esecuzione del codice dell'app da altre app.
  • Un framework per applicazioni con implementazioni solide di funzionalità di sicurezza comuni come crittografia, autorizzazioni e comunicazione interprocessuale sicura (IPC).
  • Tecnologie come la randomizzazione del layout dello spazio degli indirizzi (ASLR), no-execute (NX), ProPolice, safe_iop, OpenBSD dlmalloc e calloc e Linux mmap_min_addr per attenuare i rischi associati a errori comuni di gestione della memoria.
  • Autorizzazioni concesse dall'utente per limitare l'accesso alle funzionalità di sistema e ai dati utente.
  • Autorizzazioni definite dall'applicazione per controllare i dati dell'applicazione su base singola app.

È importante conoscere le best practice per la sicurezza di Android riportate in questa pagina. Seguire queste prassi come abitudini di programmazione generali ti aiuta a evitare di introdurre inavvertitamente problemi di sicurezza che influiscono negativamente sui tuoi utenti.

Autenticazione

L'autenticazione è un prerequisito per molte operazioni di sicurezza chiave. Per controllare l'accesso agli asset protetti, come i dati utente, la funzionalità dell'app e altre risorse, dovrai aggiungere l'autenticazione alla tua app per Android.

Puoi migliorare l'esperienza di autenticazione dell'utente integrando la tua app con Credential Manager. Gestore delle credenziali è una libreria Android Jetpack che unifica il supporto dell'API per la maggior parte dei principali metodi di autenticazione, tra cui passkey, password e soluzioni di accesso federato come Accedi con Google.

Per migliorare ulteriormente la sicurezza della tua app, ti consigliamo di aggiungere metodi di autenticazione biometrica come le scansioni dell'impronta o il riconoscimento facciale. App come quelle per la gestione di finanze, salute o identità potrebbero essere ottime candidate per l'aggiunta dell'autenticazione biometrica.

Il framework di compilazione automatica di Android può semplificare la procedura di registrazione e accesso, riducendo i tassi di errore e le difficoltà degli utenti. La compilazione automatica si integra con i gestori delle password, consentendo agli utenti di selezionare password complesse e casuali che possono essere memorizzate e recuperate in modo facile e sicuro.

Integrità dell'app

L'API Play Integrity ti aiuta a controllare che le interazioni e le richieste del server provengano dal programma binario dell'app originale in esecuzione su un dispositivo Android originale. Rilevando interazioni potenzialmente rischiose e fraudolente, ad esempio quelle provenienti da versioni dell'app manomesse e ambienti non attendibili, il server di backend della tua app può rispondere con azioni appropriate per prevenire attacchi e ridurre gli abusi.

Archiviazione dei dati

Il problema di sicurezza più comune per un'applicazione su Android è se i dati salvati sul dispositivo sono accessibili ad altre app. Esistono tre modi fondamentali per salvare i dati sul dispositivo:

  • Memoria interna
  • L'unità di archiviazione esterna
  • Fornitori di contenuti

Le sezioni seguenti descrivono i problemi di sicurezza associati a ciascun approccio.

Memoria interna

Per impostazione predefinita, i file che crei nella memoria interna sono accessibili solo alla tua app. Android implementa questa protezione, che è sufficiente per la maggior parte delle applicazioni.

Evita le modalità MODE_WORLD_WRITEABLE e MODE_WORLD_READABLE non più supportate per i file IPC. Non consentono di limitare l'accesso ai dati a determinate applicazioni e non forniscono alcun controllo sul formato dei dati. Se vuoi condividere i tuoi dati con altri processi dell'app, considera la possibilità di utilizzare un fornitore di contenuti, che offre autorizzazioni di lettura e scrittura ad altre app e può concedere autorizzazioni dinamiche su base casistica.

L'unità di archiviazione esterna

I file creati su spazio di archiviazione esterno, come le schede SD, sono leggibili e scrivibili a livello globale. Poiché lo spazio di archiviazione esterno può essere rimosso dall'utente e anche modificato da qualsiasi applicazione, memorizza solo informazioni non sensibili utilizzando lo spazio di archiviazione esterno.

Esegui la convalida dell'input quando gestisci i dati dello spazio di archiviazione esterno come faresti con i dati di qualsiasi origine non attendibile. Non memorizzare file eseguibili o di classi su archiviazione esterna prima del caricamento dinamico. Se la tua app recupera file eseguibili dallo spazio di archiviazione esterno, assicurati che i file siano firmati e sottoposti a verifica crittografica prima del caricamento dinamico.

Fornitori di contenuti

I fornitori di contenuti offrono un meccanismo di archiviazione strutturato che può essere limitato alla tua applicazione o esportato per consentire l'accesso da parte di altre applicazioni. Se non intendi fornire ad altre applicazioni l'accesso al tuo ContentProvider, contrassegnalo come android:exported=false nel file manifest dell'applicazione. In caso contrario, imposta l'attributo android:exported su true per consentire ad altre app di accedere ai dati archiviati.

Quando crei un ContentProvider che viene esportato per essere utilizzato da altre applicazioni, puoi specificare una singola autorizzazione per la lettura e la scrittura oppure autorizzazioni distinte per la lettura e la scrittura. Limita le autorizzazioni a quelle necessarie per svolgere l'attività in questione. Tieni presente che solitamente è più facile aggiungere in un secondo momento le autorizzazioni per esporre nuove funzionalità rispetto a rimuoverle e influire sugli utenti esistenti.

Se utilizzi un fornitore di contenuti per condividere dati solo tra le tue app, ti consigliamo di utilizzare l'attributo android:protectionLevel impostato su signature Protezione. Le autorizzazioni di firma non richiedono conferma da parte dell'utente, pertanto offrono un'esperienza utente migliore e un accesso più controllato ai dati dei fornitori di contenuti quando le app che accedono ai dati sono firmate con la stessa chiave.

I fornitori di contenuti possono anche fornire un accesso più granulare dichiarando l'attributo android:grantUriPermissions e utilizzando i flag FLAG_GRANT_READ_URI_PERMISSION e FLAG_GRANT_WRITE_URI_PERMISSION nell'oggetto Intent che attiva il componente. L'ambito di queste autorizzazioni può essere ulteriormente limitato dall'elemento <grant-uri-permission>.

Quando accedi a un fornitore di contenuti, utilizza metodi di query con parametri come query, update e delete() per evitare potenziali SQL injection da origini non attendibili. Tieni presente che l'utilizzo di metodi con parametri non è sufficiente se l'argomento selection viene creato concatenando i dati utente prima di inviarli al metodo.

Non avere un falso senso di sicurezza in merito all'autorizzazione di scrittura. L'autorizzazione di scrittura consente di utilizzare istruzioni SQL che consentono di confermare alcuni dati mediante clausole WHERE delle creatività e l'analisi dei risultati. Ad esempio, un malintenzionato potrebbe verificare la presenza di un numero di telefono specifico in un log delle chiamate modificando una riga solo se il numero di telefono esiste già. Se i dati del fornitore di contenuti hanno una struttura prevedibile, l'autorizzazione di scrittura potrebbe essere equivalente a fornire sia la lettura sia la scrittura.

Autorizzazioni

Poiché Android esegue il sandboxing delle applicazioni, queste devono condividere esplicitamente risorse e dati. A tal fine, dichiarano le autorizzazioni di cui hanno bisogno per funzionalità aggiuntive non fornite dalla sandbox di base, incluso l'accesso alle funzionalità del dispositivo come la fotocamera.

Richieste di autorizzazione

Riduci al minimo il numero di autorizzazioni richieste dalla tua app. La limitazione dell'accesso alle autorizzazioni sensibili riduce il rischio di utilizzo improprio involontario di queste autorizzazioni, migliora l'adozione da parte degli utenti e rende l'app meno vulnerabile agli attaccanti. In genere, se un'autorizzazione non è necessaria per il funzionamento della tua app, non richiederla. Consulta la guida per valutare se la tua app deve dichiarare le autorizzazioni.

Se possibile, progetta l'applicazione in modo che non richieda autorizzazioni. Ad esempio, anziché richiedere l'accesso alle informazioni del dispositivo per creare un identificatore univoco, crea un UUID per la tua applicazione. Scopri di più nella sezione sui dati utente. In alternativa, anziché utilizzare lo spazio di archiviazione esterno (che richiede l'autorizzazione), archivia i dati nello spazio di archiviazione interno.

Oltre a richiedere le autorizzazioni, la tua applicazione può utilizzare l'elemento <permission> per proteggere le comunicazioni interprocessuali sensibili alla sicurezza ed esposte ad altre applicazioni, ad esempio un ContentProvider. In generale, se possibile, consigliamo di utilizzare controlli di accesso diversi dalle autorizzazioni confermate dall'utente, in quanto le autorizzazioni possono creare confusione negli utenti. Ad esempio, valuta la possibilità di utilizzare il livello di protezione delle firme per le autorizzazioni per la comunicazione IPC tra le applicazioni fornite da un singolo sviluppatore.

Non divulgare dati protetti da autorizzazioni. Ciò si verifica quando l'app espone dati tramite IPC che sono disponibili solo perché l'app ha l'autorizzazione per accedervi. I client dell'interfaccia IPC della tua app potrebbero non disporre della stessa autorizzazione di accesso ai dati. Ulteriori dettagli sulla frequenza e sui potenziali effetti di questo problema sono disponibili nel paper di ricerca Permission Re-Delegation: Attacks and Defenses, pubblicato su USENIX.

Definizioni delle autorizzazioni

Definisci il più piccolo insieme di autorizzazioni che soddisfa i tuoi requisiti di sicurezza. La creazione di una nuova autorizzazione è relativamente insolita per la maggior parte delle applicazioni, perché le autorizzazioni predefinite dal sistema coprono molte situazioni. Ove opportuno, esegui controlli di accesso utilizzando le autorizzazioni esistenti.

Se hai bisogno di una nuova autorizzazione, valuta se puoi svolgere la tua attività con un livello di protezione della firma. Le autorizzazioni di firma sono trasparenti per l'utente e consentono l'accesso solo da parte di applicazioni firmate dallo stesso sviluppatore dell'applicazione che esegue il controllo delle autorizzazioni.

Se è comunque necessaria la creazione di una nuova autorizzazione, dichiarala nel file manifest dell'app utilizzando l'elemento <permission>. Le app che utilizzano la nuova autorizzazione possono farvi riferimento aggiungendo un elemento <uses-permission> nei file manifest. Puoi anche aggiungere le autorizzazioni in modo dinamico utilizzando il metodo addPermission().

Se crei un'autorizzazione con il livello di protezione pericoloso, devi prendere in considerazione una serie di complessità:

  • L'autorizzazione deve avere una stringa che esprima in modo conciso all'utente la decisione di sicurezza che deve prendere.
  • La stringa di autorizzazione deve essere localizzata in molte lingue diverse.
  • Gli utenti potrebbero scegliere di non installare un'applicazione perché un'autorizzazione è confusa o percepita come rischiosa.
  • Le applicazioni potrebbero richiedere l'autorizzazione se l'autore dell'autorizzazione non è stato installato.

Ognuna di queste rappresenta una sfida non tecnica significativa per te come sviluppatore e allo stesso tempo confonde gli utenti, motivo per cui sconsigliamo l'uso del livello di autorizzazione pericoloso.

Reti

Le transazioni di rete sono intrinsecamente rischiose per la sicurezza, perché comportano la trasmissione di dati potenzialmente privati dell'utente. Le persone sono sempre più consapevoli dei problemi di privacy di un dispositivo mobile, in particolare quando il dispositivo esegue transazioni di rete, pertanto è molto importante che la tua app implementi tutte le best practice per mantenere sempre al sicuro i dati dell'utente.

Networking IP

La rete su Android non è molto diversa da altri ambienti Linux. L'aspetto fondamentale è assicurarsi che vengano utilizzati protocolli appropriati per i dati sensibili, ad esempio HttpsURLConnection per il traffico web sicuro. Utilizza HTTPS anziché HTTP ovunque HTTPS sia supportato sul server, poiché i dispositivi mobili si connettono spesso a reti non protette, come gli hotspot Wi-Fi pubblici.

La comunicazione autenticata e criptata a livello di socket può essere implementata facilmente utilizzando la classe SSLSocket. Data la frequenza con cui i dispositivi Android si connettono a reti wireless non sicure tramite Wi-Fi, l'utilizzo di reti sicure è vivamente consigliato per tutte le applicazioni che comunicano sulla rete.

Alcune applicazioni utilizzano le porte di rete localhost per gestire comunicazioni IPC sensibili. Non utilizzare questo approccio, perché queste interfacce sono accessibili da altre applicazioni sul dispositivo. Utilizza invece un meccanismo IPC di Android in cui è possibile l'autenticazione, ad esempio con un Service. L'associazione all'INADDR_ANY indirizzo IP non specifico è peggiore dell'utilizzo del loopback, perché consente all'applicazione di ricevere richieste da qualsiasi indirizzo IP.

Assicurati di non considerare attendibili i dati scaricati da HTTP o da altri protocolli non sicuri. Sono incluse la convalida dell'input in WebView e le eventuali risposte agli intent emessi tramite HTTP.

Rete di telefonia

Il protocollo Short Message Service (SMS) è stato progettato principalmente per le comunicazioni tra utenti e non è adatto per le app che vogliono trasferire dati. A causa delle limitazioni degli SMS, ti consigliamo di utilizzare Firebase Cloud Messaging (FCM) e la rete IP per inviare messaggi di dati da un server web alla tua app su un dispositivo dell'utente.

Tieni presente che gli SMS non sono né criptati né autenticati con crittografia a prova di manomissione sulla rete o sul dispositivo. In particolare, qualsiasi destinatario di SMS deve aspettarsi che un utente malintenzionato possa aver inviato l'SMS alla tua applicazione. Non fare affidamento su dati SMS non autenticati per eseguire comandi sensibili. Tieni inoltre presente che gli SMS possono essere soggetti a spoofing e/o intercettazione sulla rete. Sul dispositivo Android stesso, i messaggi SMS vengono trasmessi come intent di trasmissione, pertanto possono essere letti o acquisiti da altre applicazioni che dispongono dell'autorizzazione READ_SMS.

Convalida dell'input

La convalida insufficiente degli input è uno dei problemi di sicurezza più comuni che interessano le applicazioni, indipendentemente dalla piattaforma su cui vengono eseguite. Android dispone di contromisure a livello di piattaforma che riducono l'esposizione delle applicazioni a problemi di convalida degli input e ti consigliamo di utilizzare queste funzionalità, se possibile. Inoltre, ti consigliamo di utilizzare linguaggi sicuri per i tipi per ridurre la probabilità di problemi di convalida dell'input.

Se utilizzi il codice nativo, tutti i dati letti dai file, ricevuti tramite la rete o ricevuti da un IPC possono potenzialmente introdurre un problema di sicurezza. I problemi più comuni sono sovrieflussi del buffer, uso dopo il rilascio ed errori di tipo off-by-one. Android offre una serie di tecnologie, come ASLR e DEP (Data Execution Prevention), che riducono la possibilità di sfruttare questi errori, ma non risolvono il problema di fondo. Puoi evitare queste vulnerabilità gestendo con attenzione i puntatori e i buffer.

Anche i linguaggi dinamici basati su stringhe come JavaScript e SQL sono soggetti a problemi di convalida dell'input a causa di caratteri di escape e script baiting.

Se utilizzi i dati all'interno di query inviate a un database SQL o a un fornitore di contenuti, l'attacco di SQL injection può essere un problema. La difesa migliore è utilizzare query parametrizzate, come discusso nella sezione sui fornitori di contenuti. Limitare le autorizzazioni a sola lettura o sola scrittura può anche ridurre il potenziale danno correlato all'iniezione SQL.

Se non riesci a utilizzare le funzionalità di sicurezza descritte in questa sezione, assicurati di utilizzare formati di dati ben strutturati e verifica che i dati siano conformi al formato previsto. Sebbene il blocco di caratteri specifici o la sostituzione di caratteri possa essere una strategia efficace, queste tecniche sono soggette a errori nella pratica e ti consigliamo di evitarle, se possibile.

Dati utente

L'approccio migliore per la sicurezza dei dati utente è ridurre al minimo l'utilizzo di API che accedono a informazioni sensibili o personali. Se hai accesso ai dati utente, se possibile, evita di memorizzarli o trasmetterli. Valuta se la logica dell'applicazione può essere implementata utilizzando un hash o una forma non reversibile dei dati. Ad esempio, la tua app potrebbe utilizzare l'hash di un indirizzo email come chiave principale per evitare di trasmettere o memorizzare l'indirizzo email. In questo modo riduci le probabilità di esporre inavvertitamente i dati e anche le probabilità che gli autori di attacchi tentino di sfruttare la tua app.

Autentica l'utente ogni volta che è richiesto l'accesso a dati privati e utilizza metodi di autenticazione moderni come le passkey e Credential Manager. Se la tua app deve accedere a informazioni personali, tieni presente che alcune giurisdizioni potrebbero richiedere di fornire norme sulla privacy che spieghino come utilizzi e memorizzi questi dati. Segui la best practice di sicurezza che prevede di ridurre al minimo l'accesso ai dati utente per semplificare la conformità.

Inoltre, valuta se la tua applicazione potrebbe esporre inavvertitamente informazioni personali a terze parti, ad esempio componenti di terze parti per la pubblicità o servizi di terze parti utilizzati dalla tua applicazione. Se non sai perché un componente o un servizio richiede informazioni personali, non fornirle. In generale, ridurre l'accesso alle informazioni personali da parte della tua applicazione riduce la potenziale presenza di problemi in questo ambito.

Se la tua app richiede l'accesso a dati sensibili, valuta se devi trasmetterli a un server o se puoi eseguire l'operazione sul client. Valuta la possibilità di eseguire qualsiasi codice che utilizza dati sensibili sul client per evitare di trasmettere i dati degli utenti. Inoltre, assicurati di non esporre inavvertitamente i dati utente ad altre applicazioni sul dispositivo tramite IPC eccessivamente permissivi, file scrivibili da tutti o socket di rete. La comunicazione interprocessuale eccessivamente permissiva è un caso speciale di fuga di dati protetti da autorizzazioni, discusso nella sezione Richieste di autorizzazione.

Se è richiesto un identificatore univoco globale (GUID), crea un numero grande e univoco e memorizzalo. Non utilizzare identificatori dello smartphone, come il numero di telefono o l'IMEI, che potrebbero essere associati a informazioni personali. Questo argomento viene discusso in modo più dettagliato nella pagina sulle best practice per gli identificatori univoci.

Fai attenzione quando scrivi nei log sul dispositivo. Su Android, i log sono una risorsa condivisa e sono disponibili per un'applicazione con l'autorizzazione READ_LOGS. Anche se i dati dei log dello smartphone sono temporanei e vengono cancellati al riavvio, un logging improprio delle informazioni dell'utente potrebbe inavvertitamente divulgare i dati utente ad altre applicazioni. Oltre a non registrare PII, limita l'utilizzo dei log nelle app di produzione. Per implementarlo facilmente, utilizza flag di debug e classi Log personalizzate con livelli di logging facilmente configurabili.

WebView

Poiché WebView utilizza contenuti web che possono includere HTML e JavaScript, un uso improprio può introdurre problemi comuni di sicurezza web come il cross-site scripting (iniezione di JavaScript). Android include una serie di meccanismi per ridurre l'ambito di questi potenziali problemi limitando la funzionalità di WebView alle funzionalità minime richieste dalla tua applicazione.

Se la tua applicazione non utilizza direttamente JavaScript all'interno di un WebView, non chiamare setJavaScriptEnabled. Alcuni esempi di codice utilizzano questo metodo. Se riutilizzi un codice di esempio che lo utilizza in un'applicazione di produzione, rimuovi la chiamata al metodo se non è necessaria. Per impostazione predefinita, WebView non esegue JavaScript, pertanto gli attacchi XSS non sono possibili.

Utilizza addJavaScriptInterface() con particolare attenzione, perché consente a JavaScript di richiamare operazioni normalmente riservate alle applicazioni Android. Se lo utilizzi, esponi addJavaScriptInterface() solo a pagine web da cui tutti gli input sono attendibili. Se sono consentiti input non attendibili, il codice JavaScript non attendibile potrebbe essere in grado di invocare metodi Android all'interno della tua app. In generale, consigliamo di esporre addJavaScriptInterface() solo al codice JavaScript contenuto nell'APK dell'applicazione.

Se la tua applicazione accede a dati sensibili con un WebView, ti consigliamo di utilizzare il metodo clearCache() per eliminare i file archiviati localmente. Puoi anche utilizzare le intestazioni lato server, ad esempio no-store, per indicare che un'applicazione non deve memorizzare nella cache determinati contenuti.

I dispositivi con piattaforme precedenti ad Android 4.4 (livello API 19) utilizzano una versione di webkit che presenta una serie di problemi di sicurezza. Come soluzione alternativa, se la tua app viene eseguita su questi dispositivi, deve confermare che gli oggetti WebView mostrino solo contenuti attendibili. Per assicurarti che la tua app non sia esposta a potenziali vulnerabilità in SSL, utilizza l'oggetto di sicurezza aggiornabile Provider come descritto in Aggiornare il provider di sicurezza per proteggersi dagli exploit SSL. Se la tua applicazione deve eseguire il rendering dei contenuti del web aperto, considera la possibilità di fornire il tuo renderer in modo da poterlo mantenere aggiornato con le patch di sicurezza più recenti.

Richieste di credenziali

Le richieste di credenziali sono un vettore di attacco. Ecco alcuni suggerimenti per aiutarti a rendere più sicure le richieste di credenziali nelle tue app per Android.

Riduci al minimo l'esposizione delle credenziali

  • Evita richieste di credenziali non necessarie. Per rendere gli attacchi di phishing più evidenti e meno probabili di avere successo, riduci al minimo la frequenza con cui chiedi le credenziali utente. Utilizza invece un token di autorizzazione e aggiornalo. Richiedi solo la quantità minima di informazioni sulle credenziali necessarie per l'autenticazione e l'autorizzazione.
  • Memorizza le credenziali in modo sicuro. Utilizza Gestore delle credenziali per attivare l'autenticazione senza password utilizzando le passkey o per implementare l'accesso federato utilizzando schemi come Accedi con Google. Se devi utilizzare l'autenticazione con password tradizionale, non memorizzare ID utente e password sul dispositivo. Esegui invece l'autenticazione iniziale utilizzando il nome utente e la password forniti dall'utente, quindi utilizza un token di autorizzazione specifico per il servizio di breve durata.
  • Limita l'ambito delle autorizzazioni. Non richiedere autorizzazioni ampie per un'attività che richiede solo un ambito più ristretto.
  • Limita i token di accesso. Utilizza chiamate API e operazioni con token di breve durata.
  • Limita le frequenze di autenticazione. Richieste di autenticazione o autorizzazione rapide e successive possono essere un segnale di un attacco di forza bruta. Limita queste tariffe a una frequenza ragionevole, consentendo al contempo un'esperienza nell'app funzionale e user-friendly.

Utilizzare l'autenticazione sicura

  • Implementa le passkey. Attiva le passkey come upgrade più sicuro e intuitivo alle password.
  • Aggiungi i dati biometrici. Offri la possibilità di utilizzare l'autenticazione biometrica come il riconoscimento dell'impronta o facciale per una maggiore sicurezza.
  • Utilizza provider di identità federati. Gestore delle credenziali supporta i provider di autenticazione federati come Accedi con Google.
  • Crittografa le comunicazioni Utilizza HTTPS e tecnologie simili per assicurarti che i dati inviati dalla tua app tramite una rete siano protetti.

Applica la gestione sicura dell'account

  • Collegarti a servizi accessibili a più applicazioni utilizzando AccountManager. Utilizza la classe AccountManager per richiamare un servizio basato su cloud e non memorizzare le password sul dispositivo.
  • Dopo aver utilizzato AccountManager per recuperare un Account, utilizza CREATOR prima di passare le credenziali per non trasmetterle inavvertitamente all'applicazione sbagliata.
  • Se le credenziali vengono utilizzate solo dalle applicazioni che crei, puoi verificare l'applicazione che accede a AccountManager utilizzando checkSignatures. In alternativa, se solo un'applicazione utilizza la credenza, puoi utilizzare un KeyStore per lo spazio di archiviazione.

Resta vigile

  • Mantieni aggiornato il codice. Assicurati di aggiornare il codice sorgente, incluse eventuali librerie e dipendenze di terze parti, per proteggerti dalle vulnerabilità più recenti.
  • Monitora le attività sospette. Cerca potenziali usi impropri, ad esempio schemi di uso improprio dell'autorizzazione.
  • Esegui la revisione del codice. Esegui controlli di sicurezza regolari sul codice di base per verificare la presenza di potenziali problemi di richiesta di credenziali.

Gestione delle chiavi API

Le chiavi API sono un componente fondamentale di molte app per Android, in quanto consentono di accedere a servizi esterni ed eseguire funzioni essenziali come la connessione a servizi di mappatura, autenticazione e meteo. Tuttavia, l'esposizione di queste chiavi sensibili può avere gravi conseguenze, tra cui violazioni dei dati, accessi non autorizzati e perdite finanziarie. Per evitare questi scenari, gli sviluppatori devono implementare strategie sicure per la gestione delle chiavi API durante il processo di sviluppo.

Per proteggere i servizi da usi impropri, le chiavi API devono essere protette con attenzione. Per proteggere una connessione tra l'app e un servizio che utilizza una chiave API, devi proteggere l'accesso all'API. Quando l'app viene compilata e il codice sorgente include le chiavi API, è possibile che un malintenzionato decompili l'app e trovi queste risorse.

Questa sezione è rivolta a due gruppi di sviluppatori Android: quelli che lavorano con i team di infrastruttura sulla pipeline di distribuzione continua e quelli che eseguono il deployment di app autonome nel Play Store. Questa sezione illustra le best practice per gestire le chiavi API in modo che la tua app possa comunicare in modo sicuro con i servizi.

Generazione e archiviazione

Gli sviluppatori devono considerare lo spazio di archiviazione delle chiavi API come un componente fondamentale della protezione dei dati e della privacy degli utenti, adottando un approccio di difesa in profondità.

Archiviazione sicura delle chiavi

Per una sicurezza ottimale della gestione delle chiavi, utilizza l'archivio chiavi Android e cripta le chiavi memorizzate utilizzando uno strumento affidabile come Tink Java.

Esclusione dal controllo del codice sorgente

Non eseguire mai il commit delle chiavi API nel repository del codice sorgente. L'aggiunta di chiavi API al codice sorgente comporta il rischio di esposizione delle chiavi a repository pubblici, esempi di codice condivisi e file condivisi per errore. Utilizza invece i plug-in Gradle come secrets-gradle-plugin per lavorare con le chiavi API nel tuo progetto.

Chiavi specifiche per l'ambiente

Se possibile, utilizza ambienti di sviluppo, test e produzione con chiavi API separate. Utilizza chiavi specifiche per ogni ambiente per isolare ciascun ambiente, riducendo il rischio di esporre i dati di produzione e consentendoti di disattivare le chiavi compromesse senza influire sull'ambiente di produzione.

Utilizzo e controllo dell'accesso

Le best practice per le chiavi API sicure sono essenziali per proteggere la tua API e i tuoi utenti. Ecco come preparare le chiavi per una sicurezza ottimale:

  • Genera chiavi univoche per ogni app: utilizza chiavi API separate per ogni app per contribuire a identificare e isolare l'accesso compromesso.
  • Implementa le restrizioni IP: se possibile, limita l'utilizzo della chiave API a indirizzi IP o intervalli specifici.
  • Limita l'utilizzo della chiave per le app mobile: limita l'utilizzo della chiave API ad app mobile specifiche raggruppandole con la chiave o utilizzando i certificati delle app.
  • Registra e monitora le attività sospette: implementa meccanismi di monitoraggio e registrazione dell'utilizzo dell'API per rilevare attività sospette e prevenire potenziali abusi.

Nota: il tuo servizio deve fornire funzionalità per limitare le chiavi a un determinato pacchetto o piattaforma. Ad esempio, l'API Google Maps limita l'accesso alle chiavi in base al nome del pacchetto e alla chiave di firma.

OAuth 2.0 fornisce un framework per autorizzare l'accesso alle risorse. Definisce gli standard per l'interazione tra client e server e consente l'autorizzazione sicura. Puoi utilizzare OAuth 2.0 per limitare l'utilizzo delle chiavi API a client specifici e definire l'ambito di accesso in modo che ogni chiave API abbia solo il livello minimo di accesso richiesto per lo scopo previsto.

Rotazione e scadenza delle chiavi

Per ridurre il rischio di accessi non autorizzati tramite vulnerabilità dell'API non scoperte, è importante ruotare regolarmente le chiavi API. Lo standard ISO 27001 definisce un framework di conformità per la frequenza con cui eseguire la rotazione delle chiavi. Nella maggior parte dei casi, un periodo di rotazione delle chiavi compreso tra 90 giorni e 6 mesi dovrebbe essere sufficiente. L'implementazione di un sistema di gestione delle chiavi efficace può aiutarti a semplificare queste procedure, migliorando l'efficienza della rotazione e della scadenza delle chiavi.

Best practice generali

  • Utilizza SSL/HTTPS: utilizza sempre la comunicazione HTTPS per criptare le richieste dell'API.
  • Pinning dei certificati: per un ulteriore livello di sicurezza, puoi valutare la possibilità di implementare il pinning dei certificati per verificare quali certificati sono considerati validi.
  • Convalida e rimuovi gli input degli utenti: convalida e rimuovi gli input degli utenti per prevenire attacchi di inserimento che potrebbero esporre le chiavi API.
  • Segui le best practice per la sicurezza: implementa le best practice di sicurezza generali nel tuo processo di sviluppo, tra cui tecniche di codifica sicura, revisioni del codice e scansione delle vulnerabilità.
  • Rimani al corrente: mantieni aggiornate le tue conoscenze sulle ultime minacce alla sicurezza e sulle migliori pratiche per la gestione delle chiavi API.
  • SDK aggiornati: assicurati che gli SDK e le librerie siano aggiornati alla versione più recente.

Crittografia

Oltre a fornire l'isolamento dei dati, a supportare la crittografia completa del file system e a fornire canali di comunicazione sicuri, Android offre un'ampia gamma di algoritmi per proteggere i dati utilizzando la crittografia.

Scopri quali fornitori di sicurezza Java Cryptography Architecture (JCA) utilizza il tuo software. Prova a utilizzare il livello più alto dell'implementazione del framework preesistente che può supportare il tuo caso d'uso. Se applicabile, utilizza i fornitori forniti da Google nell'ordine specificato da Google.

Se devi recuperare in modo sicuro un file da una posizione di rete nota, un semplice URI HTTPS potrebbe essere sufficiente e non richiede alcuna conoscenza di crittografia. Se hai bisogno di un tunnel sicuro, valuta la possibilità di utilizzare HttpsURLConnection o SSLSocket anziché scrivere il tuo protocollo. Se utilizzi SSLSocket, tieni presente che non esegue la verifica del nome host. Consulta gli avvisi sull'utilizzo diretto di SSLSocket.

Se ritieni di dover implementare il tuo protocollo, non implementare i tuoi algoritmi crittografici. Utilizza algoritmi crittografici esistenti, come le implementazioni di AES e RSA fornite nella classe Cipher. Inoltre, segui queste best practice:

  • Utilizza AES a 256 bit per scopi commerciali. Se non è disponibile, utilizza AES a 128 bit.
  • Utilizza dimensioni delle chiavi pubbliche di 224 o 256 bit per la crittografia con curva ellittica (EC).
  • Scopri quando utilizzare le modalità di blocco CBC, CTR o GCM.
  • Evita il riutilizzo di IV/contatori in modalità CTR. Assicurati che siano crittograficamente casuali.
  • Quando utilizzi la crittografia, implementa l'integrità utilizzando la modalità CBC o CTR con una delle seguenti funzioni:
    • HMAC-SHA1
    • HMAC-SHA-256
    • HMAC-SHA-512
    • Modalità GCM

Utilizza un generatore di numeri casuali sicuro, SecureRandom, per inizializzare le chiavi crittografiche generate da KeyGenerator. L'utilizzo di una chiave non generata con un generatore di numeri casuali sicuro indebolisce notevolmente l'efficacia dell'algoritmo e potrebbe consentire attacchi offline.

Se devi memorizzare una chiave per un uso ripetuto, utilizza un meccanismo, come KeyStore, che fornisca archiviazione e recupero a lungo termine delle chiavi cryptographic.

Comunicazione tra processi

Alcune app tentano di implementare l'IPC utilizzando tecniche Linux tradizionali come socket di rete e file condivisi. Tuttavia, ti consigliamo di utilizzare la funzionalità di sistema Android per l'IPC, ad esempio Intent, Binder o Messenger con un Service e BroadcastReceiver. I meccanismi IPC di Android ti consentono di verificare l'identità dell'applicazione che si connette al tuo IPC e di impostare i criteri di sicurezza per ciascun meccanismo IPC.

Molti degli elementi di sicurezza sono condivisi tra i meccanismi IPC. Se il meccanismo IPC non è destinato all'utilizzo da parte di altre applicazioni, imposta l'attributo android:exported su false nell'elemento manifest del componente, ad esempio per l'elemento <service>. Questo è utile per le applicazioni che consistono in più processi all'interno dello stesso UID o se decidi in un secondo momento durante lo sviluppo di non voler effettivamente esporre la funzionalità come IPC, ma non vuoi riscriverne il codice.

Se l'IPC è accessibile ad altre applicazioni, puoi applicare un criterio di sicurezza utilizzando l'elemento <permission>. Se la comunicazione interprocessuale avviene tra app di tua proprietà e firmate con la stessa chiave, utilizza un'autorizzazione signature-level in android:protectionLevel.

Situazioni

Per le attività e i broadcast receiver, gli intent sono il meccanismo preferito per la comunicazione interprocessuale asincrona su Android. A seconda dei requisiti dell'applicazione, potresti utilizzare sendBroadcast, sendOrderedBroadcast o un'intent esplicita per un componente dell'applicazione specifico. Per motivi di sicurezza, sono preferibili le intenzioni esplicite.

Attenzione: se utilizzi un'intent per eseguire il binding a un **Service**, utilizza un'intent esplicita per mantenere sicura la tua app. L'utilizzo di un'intent implicita per avviare un servizio rappresenta un rischio per la sicurezza, perché non puoi sapere con certezza quale servizio risponderà all'intent e l'utente non può vedere quale servizio viene avviato. A partire da Android 5.0 (livello API 21), il sistema genera un'eccezione se chiami **bindService()** con un'intent implicita.

Tieni presente che le trasmissioni ordinate possono essere consumate da un destinatario, pertanto potrebbero non essere inviate a tutte le applicazioni. Se invii un'intenzione che deve essere comunicata a un destinatario specifico, devi utilizzare un'intenzione esplicita che dichiari il destinatario per nome.

I mittenti di un intent possono verificare che il destinatario disponga dell'autorizzazione specificando un'autorizzazione non null con la chiamata al metodo. Solo le applicazioni con questa autorizzazione ricevono l'intent. Se i dati all'interno di un'intenzione di trasmissione potrebbero essere sensibili, valuta la possibilità di applicare un'autorizzazione per assicurarti che le applicazioni dannose non possano registrarsi per ricevere questi messaggi senza le autorizzazioni appropriate. In queste circostanze, potresti anche prendere in considerazione l'invocazione diretta del ricevitore, anziché l'invio di una trasmissione.

Nota: i filtri di intent non sono funzionalità di sicurezza. I componenti possono essere invocati con intent espliciti e potrebbero non avere dati conformi al filtro intent. Per verificare che sia formattato correttamente per il ricevitore, il servizio o l'attività invocati, esegui la convalida dell'input all'interno del ricevitore dell'intent.

Servizi

Un Service viene spesso utilizzato per fornire funzionalità da utilizzare per altre applicazioni. Ogni classe di servizio deve avere una dichiarazione <service> corrispondente nel file manifest.

Per impostazione predefinita, i servizi non vengono esportati e non possono essere richiamati da altre applicazioni. Tuttavia, se aggiungi filtri di intent alla dichiarazione del servizio, viene esportato per impostazione predefinita. Ti consigliamo di dichiarare esplicitamente l'attributo android:exported per assicurarti che funzioni come previsto. I servizi possono essere protetti anche utilizzando l'attributo android:permission. In questo modo, altre applicazioni devono dichiarare un elemento corrispondente <uses-permission> nel proprio manifest per poter avviare, interrompere o eseguire il binding al servizio.

Nota: se la tua app ha come target Android 5.0 (livello API 21) o versioni successive, utilizza **JobScheduler** per eseguire i servizi in background.

Un servizio può proteggere le singole chiamate IPC effettuate al suo interno con le autorizzazioni. Per farlo, chiama checkCallingPermission() prima di eseguire l'implementazione della chiamata. Ti consigliamo di utilizzare le autorizzazioni declarative nel file manifest, poiché sono meno soggette a errori.

Attenzione: non confondere le autorizzazioni del client e del server. Assicurati che l'app chiamata abbia le autorizzazioni appropriate e verifica di concedere le stesse autorizzazioni all'app chiamante.

Interfacce di Binder e Messenger

L'utilizzo di Binder o Messenger è il meccanismo preferito per l'IPC stile RPC su Android. Forniscono interfacce ben definite che consentono l'autenticazione reciproca degli endpoint, se necessario.

Ti consigliamo di progettare le interfacce delle app in modo che non richiedano controlli delle autorizzazioni specifici per l'interfaccia. Gli oggetti Binder e Messenger non sono dichiarati nel file manifest dell'applicazione e, pertanto, non puoi applicare direttamente le autorizzazioni dichiarative. In genere ereditano le autorizzazioni dichiarate nel file manifest dell'applicazione per Service o Activity in cui sono implementate. Se stai creando un'interfaccia che richiede autenticazione e/o controlli di accesso, devi aggiungere esplicitamente questi controlli come codice nell'interfaccia Binder o Messenger.

Se fornisci un'interfaccia che richiede controlli di accesso, utilizza checkCallingPermission() per verificare se chi effettua la chiamata ha un'autorizzazione obbligatoria. Questo è particolarmente importante prima di accedere a un servizio per conto del chiamante, poiché l'identità della tua applicazione viene passata ad altre interfacce. Se stai richiamando un'interfaccia fornita da un Service, l'invocazione di bindService() può non riuscire se non disponi dell'autorizzazione per accedere al servizio in questione. Se devi consentire a un processo esterno di interagire con la tua app, ma questo non dispone delle autorizzazioni necessarie, puoi utilizzare il metodo clearCallingIdentity(). Questo metodo esegue la chiamata all'interfaccia della tua app come se fosse l'app stessa a effettuare la chiamata, anziché l'utente esterno che effettua la chiamata. Puoi ripristinare le autorizzazioni dell'autore della chiamata in un secondo momento con il metodo restoreCallingIdentity().

Per ulteriori informazioni su come eseguire l'IPC con un servizio, consulta Bound Services.

Broadcast receiver

Un BroadcastReceiver gestisce le richieste asincrone avviate da un Intent.

Per impostazione predefinita, i ricevitori vengono esportati e possono essere richiamati da qualsiasi altra applicazione. Se BroadcastReceiver è destinato all'utilizzo da parte di altre applicazioni, potresti voler applicare le autorizzazioni di sicurezza ai ricevitori utilizzando l'elemento <receiver> all'interno del file manifest dell'applicazione. In questo modo, le applicazioni senza le autorizzazioni appropriate non possono inviare un intent al BroadcastReceiver.

Sicurezza con codice caricato dinamicamente

Sconsigliamo vivamente di caricare codice dall'esterno dell'APK dell'applicazione. In questo modo, si aumenta notevolmente la probabilità di compromissione dell'applicazione a causa di inserimento o manomissione del codice. Inoltre, aggiunge complessità alla gestione delle versioni e al test delle applicazioni e può rendere impossibile verificare il comportamento di un'applicazione, pertanto potrebbe essere vietato in alcuni ambienti.

Se la tua applicazione carica codice in modo dinamico, la cosa più importante da tenere presente è che il codice caricato in modo dinamico viene eseguito con le stesse autorizzazioni di sicurezza dell'APK dell'applicazione. L'utente decide di installare la tua applicazione in base alla tua identità e si aspetta che tu fornisca qualsiasi codice eseguito all'interno dell'applicazione, incluso il codice caricato dinamicamente.

Molte applicazioni tentano di caricare codice da posizioni non sicure, ad esempio scaricate dalla rete tramite protocolli non criptati o da posizioni scrivibili da tutti, come lo spazio di archiviazione esterno. Queste posizioni potrebbero consentire a un utente della rete di modificare i contenuti in transito o a un'altra applicazione sul dispositivo di un utente di modificare i contenuti sul dispositivo. D'altra parte, i moduli inclusi direttamente nell'APK non possono essere modificati da altre applicazioni. Questo vale sia se il codice è una libreria nativa sia se è un corso caricato utilizzando DexClassLoader.

Sicurezza in una macchina virtuale

Dalvik è la macchina virtuale (VM) di runtime di Android. Dalvik è stato creato appositamente per Android, ma molti dei problemi relativi al codice sicuro in altre VM si applicano anche ad Android. In generale, non devi preoccuparti di problemi di sicurezza relativi alla macchina virtuale. L'applicazione viene eseguita in un ambiente sandbox sicuro, pertanto le altre procedure del sistema non possono accedere al codice o ai dati privati.

Se ti interessa saperne di più sulla sicurezza delle macchine virtuali, consulta la letteratura esistente sull'argomento. Due delle risorse più utilizzate sono:

Questo documento si concentra sulle aree specifiche di Android o diverse da altri ambienti VM. Per gli sviluppatori esperti di programmazione VM in altri ambienti, esistono due problemi generali che potrebbero essere diversi per la scrittura di app per Android:

  • Alcune macchine virtuali, come la JVM o il runtime .NET, fungono da confine di sicurezza, isolando il codice dalle funzionalità del sistema operativo sottostante. Su Android, la VM Dalvik non è un confine di sicurezza: la sandbox dell'applicazione è implementata a livello di sistema operativo, quindi Dalvik può interoperare con il codice nativo nella stessa applicazione senza vincoli di sicurezza.
  • Dato lo spazio di archiviazione limitato sui dispositivi mobili, è normale che gli sviluppatori vogliano creare applicazioni modulari e utilizzare il caricamento dinamico delle classi. Quando lo fai, tieni conto sia dell'origine da cui recuperi la logica dell'applicazione sia del luogo in cui la memorizzi localmente. Non utilizzare il caricamento dinamico delle classi da origini non verificate, come origini di rete non sicure o archiviazione esterna, perché il codice potrebbe essere modificato in modo da includere comportamenti dannosi.

Sicurezza nel codice nativo

In generale, consigliamo di utilizzare l'SDK Android per lo sviluppo di applicazioni, anziché il codice nativo con l'Android NDK. Le applicazioni create con codice nativo sono più complesse, meno portabili e hanno maggiori probabilità di includere errori comuni di corruzione della memoria, come gli overflow del buffer.

Android viene creato utilizzando il kernel Linux e conoscere le best practice per la sicurezza dello sviluppo di Linux è particolarmente utile se utilizzi codice nativo. Le pratiche di sicurezza di Linux non rientrano nell'ambito di questo documento, ma una delle risorse più apprezzate è Secure Programming HOWTO - Creating Secure Software.

Una differenza importante tra Android e la maggior parte degli ambienti Linux è la sandbox delle applicazioni. Su Android, tutte le applicazioni vengono eseguite nella sandbox dell'applicazione, incluse quelle scritte con codice nativo. Un buon modo per pensarci per gli sviluppatori che hanno dimestichezza con Linux è sapere che a ogni applicazione viene assegnato un identificatore utente (UID) univoco con autorizzazioni molto limitate. Questo argomento viene trattato con maggiore dettaglio nella Panoramica della sicurezza di Android e dovresti conoscere le autorizzazioni di applicazione anche se utilizzi il codice nativo.