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 tu possa in genere creare le tue app con le autorizzazioni predefinite di sistema e file ed evitare decisioni difficili in merito alla sicurezza.

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

  • La sandbox dell'app per Android, che isola i dati dell'app e l'esecuzione del codice dalle altre app.
  • Un framework applicativo con implementazioni robuste di funzionalità di sicurezza comuni come crittografia, autorizzazioni e comunicazione interprocesso (IPC) sicura.
  • Tecnologie come Address Space Layout Randomization (ASLR), No-Execute (NX), ProPolice, safe_iop, OpenBSD dlmalloc e calloc e Linux mmap_min_addr per mitigare 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 in base all'app.

È importante conoscere le best practice per la sicurezza di Android riportate in questa pagina. Seguire queste pratiche come abitudini di codifica 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 ad asset protetti come dati utente, funzionalità dell'app e altre risorse, devi aggiungere l'autenticazione alla tua app per Android.

Puoi migliorare l'esperienza di autenticazione dei tuoi utenti integrando la tua app con Gestore delle credenziali. Gestore delle credenziali è una libreria Android Jetpack che unifica il supporto 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, valuta la possibilità di aggiungere metodi di autenticazione biometrica come la scansione dell'impronta o il riconoscimento facciale. I candidati ideali per l'aggiunta dell'autenticazione biometrica potrebbero includere app per la gestione di finanze, sanità o identità.

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

Integrità dell'app

L'API Play Integrity ti aiuta a verificare che le interazioni e le richieste del server provengano dal programma binario della tua app originale in esecuzione su un dispositivo originale basato su Android. Rilevando interazioni potenzialmente rischiose e fraudolente, ad esempio da versioni manomesse dell'app 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
  • 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 deprecate per i file IPC. Non offrono la possibilità di limitare l'accesso ai dati a determinate applicazioni e non forniscono alcun controllo del formato dei dati. Se vuoi condividere i tuoi dati con altri processi dell'app, valuta la possibilità di utilizzare un content provider, che offre autorizzazioni di lettura e scrittura ad altre app e può concedere autorizzazioni dinamiche caso per caso.

Unità di archiviazione esterna

I file creati su spazio di archiviazione esterno, ad esempio le schede SD, sono leggibili e scrivibili a livello globale. Poiché la memoria esterna può essere rimossa dall'utente e modificata da qualsiasi applicazione, memorizza solo informazioni non sensibili utilizzando la memoria esterna.

Esegui la convalida dell'input quando gestisci i dati da un archivio esterno, come faresti con i dati di qualsiasi origine non attendibile. Non memorizzare file eseguibili o di classe su una memoria esterna prima del caricamento dinamico. Se la tua app recupera file eseguibili da una memoria esterna, assicurati che i file siano firmati e verificati crittograficamente 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 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 puoi specificare autorizzazioni distinte per la lettura e la scrittura. Limita le tue autorizzazioni a quelle necessarie per svolgere l'attività in corso. Tieni presente che in genere è più facile aggiungere le autorizzazioni in un secondo momento per esporre nuove funzionalità che rimuoverle e influire sugli utenti esistenti.

Se utilizzi un fornitore di contenuti per condividere i dati solo tra le tue app, ti consigliamo di utilizzare il set di attributi android:protectionLevel impostato sulla protezione signature. Le autorizzazioni di firma non richiedono la conferma dell'utente, pertanto offrono una migliore esperienza utente e un accesso più controllato ai dati del content provider 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 content provider, utilizza metodi di query con parametri come query, update e delete() per evitare potenziali SQL injection da fonti non attendibili. Tieni presente che l'utilizzo di metodi parametrizzati non è sufficiente se l'argomento selection viene creato concatenando i dati utente prima di inviarli al metodo.

Non avere una falsa sensazione di sicurezza in merito all'autorizzazione di scrittura. L'autorizzazione di scrittura consente istruzioni SQL che rendono possibile la conferma di alcuni dati utilizzando clausole WHERE creative e analizzando i risultati. Ad esempio, un malintenzionato potrebbe cercare la presenza di un numero di telefono specifico in un registro chiamate modificando una riga solo se quel 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 che la scrittura.

Autorizzazioni

Poiché Android isola le applicazioni l'una dall'altra tramite sandbox, le applicazioni devono condividere esplicitamente risorse e dati. A questo scopo, dichiarano le autorizzazioni necessarie per funzionalità aggiuntive non fornite dalla sandbox di base, incluso l'accesso a 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 un utilizzo improprio involontario di queste autorizzazioni, migliora l'adozione da parte degli utenti e rende la tua app meno vulnerabile agli attacchi. 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 la tua applicazione in modo che non richieda alcuna autorizzazione. 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 l'archiviazione esterna (che richiede l'autorizzazione), archivia i dati nella memoria interna.

Oltre a richiedere le autorizzazioni, l'applicazione può utilizzare l'elemento <permission> per proteggere l'IPC sensibile alla sicurezza ed esposto ad altre applicazioni, ad esempio un ContentProvider. In generale, consigliamo di utilizzare controlli dell'accesso diversi dalle autorizzazioni confermate dall'utente, se possibile, perché le autorizzazioni possono confondere gli utenti. Ad esempio, valuta la possibilità di utilizzare il livello di protezione della firma per le autorizzazioni per la comunicazione IPC tra applicazioni fornite da un singolo sviluppatore.

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

Definizioni delle autorizzazioni

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

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

Se è ancora necessario creare una nuova autorizzazione, dichiarala nel 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 autorizzazioni in modo dinamico utilizzando il metodo addPermission().

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

  • L'autorizzazione deve contenere 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 quando il creatore dell'autorizzazione non è stato installato.

Ciascuna di queste situazioni rappresenta una sfida non tecnica significativa per te in qualità di sviluppatore, oltre a confondere gli utenti. Per questo motivo, sconsigliamo l'utilizzo del livello di autorizzazione pericoloso.

Networking

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

Networking IP

Il networking su Android non è molto diverso da altri ambienti Linux. La considerazione principale è 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, perché i dispositivi mobili si connettono spesso a reti non protette, ad esempio reti Wi-Fi pubbliche.

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 protette tramite Wi-Fi, l'utilizzo di reti sicure è fortemente consigliato per tutte le applicazioni che comunicano tramite la rete.

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

Assicurati di non considerare attendibili i dati scaricati da HTTP o da altri protocolli non sicuri. Ciò include la convalida dell'input in WebView e di qualsiasi risposta agli intent emessi tramite HTTP.

Networking di telefonia

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

Tieni presente che gli SMS non sono criptati né autenticati in modo sicuro 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 sui dati SMS non autenticati per eseguire comandi sensibili. Inoltre, tieni presente che gli SMS possono essere soggetti a spoofing e/o intercettazione sulla rete. Sul dispositivo Android, i messaggi SMS vengono trasmessi come intent di trasmissione, quindi possono essere letti o acquisiti da altre applicazioni che dispongono dell'autorizzazione READ_SMS.

Convalida dell'input

La convalida insufficiente dell'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 dell'input e ti consigliamo di utilizzare queste funzionalità, se possibile. Inoltre, ti consigliamo di utilizzare linguaggi type-safe per ridurre la probabilità di problemi di convalida dell'input.

Se utilizzi codice nativo, tutti i dati letti da file, ricevuti tramite la rete o ricevuti da un IPC possono potenzialmente introdurre un problema di sicurezza. I problemi più comuni sono buffer overflow, use after free ed errori off-by-one. Android fornisce una serie di tecnologie, come ASLR e Prevenzione dell'esecuzione dei dati (DEP), che riducono la possibilità di sfruttare questi errori, ma non risolvono il problema di fondo. Puoi prevenire 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 dei caratteri di escape e dell'iniezione di script.

Se utilizzi dati all'interno di query inviate a un database SQL o a un content provider, l'SQL injection può rappresentare un problema. La migliore difesa è utilizzare query con parametri, come descritto nella sezione relativa ai fornitori di contenuti. La limitazione delle autorizzazioni a sola lettura o sola scrittura può anche ridurre il potenziale di danni correlati all'SQL injection.

Se non puoi 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 degli utenti, evita di archiviarli o trasmetterli, se possibile. 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 primaria per evitare di trasmettere o archiviare l'indirizzo email. In questo modo si riducono le possibilità di esporre inavvertitamente i dati e si riduce anche la possibilità che gli autori di attacchi tentino di sfruttare la tua app.

Autentica l'utente ogni volta che è necessario accedere 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 l'utilizzo e l'archiviazione di questi dati. Segui la best practice per la sicurezza di ridurre al minimo l'accesso ai dati utente per semplificare la conformità.

Inoltre, valuta se la tua applicazione potrebbe esporre inavvertitamente informazioni personali ad altre 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, la riduzione dell'accesso alle informazioni personali da parte della tua applicazione riduce il potenziale 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 utilizzando dati sensibili sul client per evitare la trasmissione dei dati utente. Inoltre, assicurati di non esporre inavvertitamente i dati utente ad altre applicazioni sul dispositivo tramite IPC eccessivamente permissivo, file scrivibili a livello globale o socket di rete. L'IPC eccessivamente permissiva è un caso speciale di perdita di dati protetti da autorizzazioni, descritto nella sezione Richieste di autorizzazioni.

Se è richiesto un GUID (Globally Unique Identifier), crea un numero grande e univoco e memorizzalo. Non utilizzare identificatori del telefono come il numero di telefono o l'IMEI, che potrebbero essere associati a informazioni personali. Questo argomento è trattato 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 del telefono sono temporanei e vengono cancellati al riavvio, la registrazione inappropriata delle informazioni dell'utente potrebbe divulgare inavvertitamente i dati dell'utente ad altre applicazioni. Oltre a non registrare PII, limita l'utilizzo dei log nelle app di produzione. Per implementare facilmente questa operazione, utilizza i flag di debug e le classi Log personalizzate con livelli di logging facilmente configurabili.

WebView

Poiché WebView utilizza contenuti web che possono includere HTML e JavaScript, un utilizzo 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 alla funzionalità minima richiesta dall'applicazione.

Se la tua applicazione non utilizza direttamente JavaScript all'interno di un WebView, non chiamare setJavaScriptEnabled. Alcuni esempi di codice campione utilizzano questo metodo. Se riutilizzi un esempio di codice campione che lo utilizza in un'applicazione di produzione, rimuovi la chiamata al metodo se non è necessaria. Per impostazione predefinita, WebView non esegue JavaScript, pertanto non è possibile eseguire attacchi XSS (cross-site scripting).

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, JavaScript non attendibile potrebbe essere in grado di richiamare metodi Android all'interno della tua app. In generale, ti consigliamo di esporre addJavaScriptInterface() solo a JavaScript contenuto nell'APK dell'applicazione.

Se la tua applicazione accede a dati sensibili con un WebView, valuta la possibilità di utilizzare il metodo clearCache() per eliminare tutti i file archiviati localmente. Puoi anche utilizzare 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 mostrano 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 di contenuti dal web aperto, valuta la possibilità di fornire un 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 rendere più sicure le richieste di credenziali nelle tue app per Android.

Ridurre al minimo l'esposizione delle credenziali

  • Evita richieste di credenziali non necessarie. Per rendere gli attacchi di phishing più evidenti e meno efficaci, riduci al minimo la frequenza con cui vengono richieste 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 e di breve durata.
  • Limitare l'ambito delle autorizzazioni. Non richiedere autorizzazioni generiche per un'attività che richiede solo un ambito più ristretto.
  • Limita i token di accesso. Utilizza operazioni e chiamate API con token di breve durata.
  • Limita i tassi di autenticazione. Richieste di autenticazione o autorizzazione rapide e successive possono essere un segnale di attacco di tipo brute force. Limita queste tariffe a una frequenza ragionevole, consentendo comunque un'esperienza nell'app funzionale e facile da usare.

Utilizzare l'autenticazione sicura

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

Pratiche di gestione sicura dell'account

  • Connettiti a servizi accessibili a più applicazioni utilizzando AccountManager. Utilizza la classe AccountManager per richiamare un servizio basato sul cloud e non memorizzare le password sul dispositivo.
  • Dopo aver utilizzato AccountManager per recuperare un Account, utilizza CREATOR prima di inserire le credenziali in modo da 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 le credenziali, puoi utilizzare un KeyStore per l'archiviazione.

Resta vigile

  • Mantieni aggiornato il codice. Assicurati di aggiornare il codice sorgente, incluse le librerie e le dipendenze di terze parti, per proteggerti dalle vulnerabilità più recenti.
  • Monitora le attività sospette. Cerca potenziali abusi, ad esempio schemi di abuso di autorizzazione.
  • Controlla il codice. Esegui controlli di sicurezza regolari sul tuo codice sorgente per verificare la presenza di potenziali problemi di richiesta delle credenziali.

Gestione delle chiavi API

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

Per proteggere i servizi da un uso improprio, 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 la tua app viene compilata e il codice sorgente include chiavi API, è possibile che un malintenzionato decompili l'app e trovi queste risorse.

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

Generazione e archiviazione

Gli sviluppatori devono considerare l'archiviazione delle chiavi API come un componente fondamentale della protezione dei dati e della privacy degli utenti utilizzando 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 archiviate utilizzando uno strumento efficace come Tink Java.

Esclusione dal controllo del codice sorgente

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

Chiavi specifiche dell'ambiente

Se possibile, utilizza chiavi API separate per gli ambienti di sviluppo, test e produzione. Utilizza chiavi specifiche per l'ambiente per isolare ogni 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 pratiche sicure per le chiavi API 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 identificare e isolare l'accesso compromesso.
  • Implementa limitazioni IP: se possibile, limita l'utilizzo della chiave API a indirizzi o intervalli IP specifici.
  • Limita l'utilizzo delle chiavi delle app mobile: limita l'utilizzo delle chiavi API a specifiche app mobile raggruppandole con la chiave o utilizzando i certificati delle app.
  • Registra e monitora le attività sospette: implementa meccanismi di registrazione e monitoraggio dell'utilizzo delle API per rilevare attività sospette e prevenire potenziali abusi.

Nota: il servizio deve fornire funzionalità per limitare le chiavi a un pacchetto o una piattaforma specifici. 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 di accesso minimo richiesto per lo scopo previsto.

Rotazione e scadenza delle chiavi

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

Best practice generali

  • Utilizza SSL/HTTPS: utilizza sempre la comunicazione HTTPS per criptare le richieste API.
  • Certificate pinning: per un ulteriore livello di sicurezza, puoi prendere in considerazione l'implementazione del certificate pinning per verificare quali certificati sono considerati validi.
  • Convalida e pulizia dell'input dell'utente: convalida e pulisci l'input dell'utente per prevenire attacchi di iniezione che potrebbero esporre le chiavi API.
  • Segui le best practice per la sicurezza: implementa le best practice generali per la sicurezza nel tuo processo di sviluppo, incluse tecniche di programmazione sicura, revisioni del codice e scansione delle vulnerabilità.
  • Rimani informato: ricevi aggiornamenti sulle ultime minacce alla sicurezza e sulle best practice per la gestione delle chiavi API.
  • SDK aggiornati: assicurati che gli SDK e le librerie siano aggiornati all'ultima versione.

Crittografia

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

Scopri quali provider di sicurezza Java Cryptography Architecture (JCA) utilizza il tuo software. Cerca di 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 conoscenze di crittografia. Se hai bisogno di un tunnel sicuro, valuta l'utilizzo di HttpsURLConnection o SSLSocket anziché scrivere il tuo protocollo. Se utilizzi SSLSocket, tieni presente che non esegue la verifica del nome host. Consulta Avvisi sull'utilizzo diretto di SSLSocket.

Se ritieni di dover implementare un tuo protocollo, non implementare i tuoi algoritmi crittografici. Utilizza algoritmi crittografici esistenti, ad esempio 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 della chiave pubblica di 224 o 256 bit per la crittografia a curva ellittica (EC).
  • Sapere quando utilizzare le modalità di blocco CBC, CTR o GCM.
  • Evita il riutilizzo di IV/contatore 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 riduce notevolmente la robustezza dell'algoritmo e potrebbe consentire attacchi offline.

Se devi memorizzare una chiave per un uso ripetuto, utilizza un meccanismo, ad esempio KeyStore, che fornisca l'archiviazione e il recupero a lungo termine delle chiavi crittografiche.

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 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 criteri di sicurezza per ogni meccanismo IPC.

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

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

Intent

Per attività e ricevitori di trasmissione, gli intent sono il meccanismo preferito per IPC asincrona su Android. A seconda dei requisiti dell'applicazione, potresti utilizzare sendBroadcast, sendOrderedBroadcast o un intent esplicito per un componente dell'applicazione specifico. Per motivi di sicurezza, sono preferiti gli intent espliciti.

Tieni presente che le trasmissioni ordinate possono essere utilizzate da un destinatario, pertanto potrebbero non essere recapitate a tutte le applicazioni. Se invii un intent che deve essere recapitato a un destinatario specifico, devi utilizzare un intent esplicito che dichiara il destinatario per nome.

I mittenti di un intent possono verificare che il destinatario disponga dell'autorizzazione specificando un'autorizzazione non nulla con la chiamata al metodo. Solo le applicazioni con questa autorizzazione ricevono l'intent. Se i dati all'interno di un intent 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'idea di richiamare direttamente il ricevitore, anziché generare una trasmissione.

Servizi

Un Service viene spesso utilizzato per fornire funzionalità da utilizzare ad 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 per intent alla dichiarazione del servizio, questi vengono esportati per impostazione predefinita. È meglio dichiarare esplicitamente l'attributo android:exported per assicurarti che si comporti nel modo previsto. I servizi possono essere protetti anche utilizzando l'attributo android:permission. In questo modo, le altre applicazioni devono dichiarare un elemento <uses-permission> corrispondente nel proprio manifest per poter avviare, arrestare o collegarsi al servizio.

Un servizio può proteggere le singole chiamate IPC effettuate con le autorizzazioni. Questa operazione viene eseguita chiamando checkCallingPermission() prima di eseguire l'implementazione della chiamata. Ti consigliamo di utilizzare le autorizzazioni dichiarative nel manifest, in quanto sono meno soggette a errori.

Interfacce Binder e Messenger

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

Ti consigliamo di progettare le interfacce della tua app in modo da non richiedere controlli delle autorizzazioni specifici per l'interfaccia. Gli oggetti Binder e Messenger non sono dichiarati nel manifest dell'applicazione, pertanto non puoi applicare autorizzazioni dichiarative direttamente a questi oggetti. In genere ereditano le autorizzazioni dichiarate nel manifest dell'applicazione per Service o Activity in cui vengono implementati. Se crei un'interfaccia che richiede l'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 dell'accesso, utilizza checkCallingPermission() per verificare se il chiamante dispone dell'autorizzazione richiesta. Ciò è particolarmente importante prima di accedere a un servizio per conto del chiamante, poiché l'identità della tua applicazione viene trasmessa ad altre interfacce. Se richiami un'interfaccia fornita da un Service, la chiamata di bindService() può non riuscire se non disponi dell'autorizzazione per accedere al servizio specificato. Se devi consentire a un processo esterno di interagire con la tua app, ma non dispone delle autorizzazioni necessarie, puoi utilizzare il metodo clearCallingIdentity(). Questo metodo esegue la chiamata all'interfaccia della tua app come se la chiamata fosse effettuata dalla tua app stessa, anziché dal chiamante esterno. Puoi ripristinare le autorizzazioni del chiamante in un secondo momento con il metodo restoreCallingIdentity().

Per saperne di più sull'esecuzione di IPC con un servizio, consulta Servizi associati.

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 il tuo BroadcastReceiver è destinato all'uso da parte di altre applicazioni, potresti voler applicare le autorizzazioni di sicurezza ai ricevitori utilizzando l'elemento <receiver> all'interno del manifest dell'applicazione. In questo modo, le applicazioni senza autorizzazioni appropriate non possono inviare un intent a BroadcastReceiver.

Sicurezza con codice caricato dinamicamente

Sconsigliamo vivamente di caricare codice da fonti esterne all'APK dell'applicazione. In questo modo aumenta notevolmente la probabilità di compromissione dell'applicazione a causa dell'iniezione o della manomissione del codice. Inoltre, aggiunge complessità alla gestione delle versioni e ai 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 a mente è 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 scaricato dalla rete tramite protocolli non criptati o da posizioni con accesso in scrittura per tutti, come l'archiviazione esterna. Queste posizioni potrebbero consentire a qualcuno sulla rete di modificare i contenuti in transito o un'altra applicazione sul dispositivo di un utente per modificare i contenuti sul dispositivo. D'altra parte, i moduli inclusi direttamente nell'APK non possono essere modificati da altre applicazioni. Ciò vale indipendentemente dal fatto che il codice sia una libreria nativa o una classe caricata utilizzando DexClassLoader.

Sicurezza in una macchina virtuale

Dalvik è la macchina virtuale (VM) di runtime di Android. Dalvik è stato creato appositamente per Android, ma molte delle preoccupazioni relative al codice sicuro in altre macchine virtuali si applicano anche ad Android. In generale, non devi preoccuparti di problemi di sicurezza relativi alla macchina virtuale. La tua applicazione viene eseguita in un ambiente sandbox sicuro, quindi altri processi sul sistema non possono accedere al tuo codice o ai tuoi dati privati.

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

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

  • Alcune macchine virtuali, come la JVM o il runtime .NET, fungono da limite di sicurezza, isolando il codice dalle funzionalità del sistema operativo sottostante. Su Android, la VM Dalvik non è un limite di sicurezza: la sandbox dell'applicazione è implementata a livello di sistema operativo, quindi Dalvik può interagire con il codice nativo nella stessa applicazione senza vincoli di sicurezza.
  • Data la limitata capacità di archiviazione sui dispositivi mobili, è comune che gli sviluppatori vogliano creare applicazioni modulari e utilizzare il caricamento dinamico delle classi. Quando lo fai, considera sia l'origine da cui recuperi la logica dell'applicazione sia la posizione in cui la memorizzi localmente. Non utilizzare il caricamento dinamico delle classi da origini non verificate, ad esempio origini di rete non protette o spazio di archiviazione esterno, perché il codice potrebbe essere modificato per includere comportamenti dannosi.

Sicurezza nel codice nativo

In generale, consigliamo di utilizzare l'SDK Android per lo sviluppo di applicazioni, anziché utilizzare codice nativo con l'Android NDK. Le applicazioni create con codice nativo sono più complesse, meno portabili e più soggette a errori comuni di danneggiamento della memoria, come i buffer overflow.

Android è basato sul kernel Linux e la conoscenza delle best practice di sicurezza per lo 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ù popolari è Secure Programming HOWTO - Creating Secure Software.

Una differenza importante tra Android e la maggior parte degli ambienti Linux è la sandbox dell'applicazione. 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 conoscono Linux è sapere che a ogni applicazione viene assegnato un identificatore utente (UID) univoco con autorizzazioni molto limitate. Questo argomento è trattato in maggiori dettagli nella Panoramica della sicurezza di Android e devi avere familiarità con le autorizzazioni delle applicazioni anche se utilizzi codice nativo.