Android si affida a un Provider
di sicurezza per
per garantire comunicazioni di rete sicure. Tuttavia, di tanto in tanto,
nel provider di sicurezza predefinito. Per proteggere da
queste vulnerabilità, Google Play
servizi consente di aggiornare automaticamente il fornitore di servizi di sicurezza
da exploit noti. Chiamando i metodi di Google Play Services, puoi contribuire a garantire
che la tua app sia in esecuzione su un dispositivo con gli ultimi aggiornamenti
proteggere dagli exploit noti.
Ad esempio, è stata scoperta una vulnerabilità in OpenSSL (CVE-2014-0224) che può lasciare le app esposte a un attacco on-path che decripta il traffico sicuro a insaputa di entrambe le parti. Versione di Google Play Services 5.0 offre una soluzione, ma le app devono verificare che la correzione sia installata. Di utilizzando i metodi di Google Play Services, puoi contribuire a garantire che la tua app sia in esecuzione su un dispositivo protetto contro l'attacco.
Attenzione: l'aggiornamento della sicurezza di un dispositivoProvider
non aggiornaandroid.net.SSLCertificateSocketFactory
,
che rimane vulnerabile. Anziché utilizzare questo corso deprecato, invitiamo gli sviluppatori di app a
utilizzano metodi ad alto livello per interagire con la crittografia, come
HttpsURLConnection
.
Applica la patch al provider di sicurezza utilizzando ProviderInstaller
Per aggiornare il provider di sicurezza di un dispositivo, utilizza la classe
ProviderInstaller
. Puoi verificare che il provider di sicurezza sia aggiornato (e aggiornarlo, se necessario) chiamando il metodo installIfNeeded()
(o installIfNeededAsync()
) della classe. Questa sezione descrive queste opzioni a livello generale. Le sezioni che seguono forniscono
passaggi ed esempi più dettagliati.
Quando chiami installIfNeeded()
,
ProviderInstaller
svolge le seguenti operazioni:
- Se
Provider
del dispositivo viene aggiornato correttamente (o è già aggiornato), il metodo restituisce senza generare un'eccezione. - Se la libreria di Google Play Services del dispositivo è obsoleta, il metodo
lanci
GooglePlayServicesRepairableException
L'app può quindi rilevare questa eccezione e mostrare all'utente una finestra di dialogo appropriata per aggiornare Google Play Services. - Se si verifica un errore non recuperabile, il metodo genera
GooglePlayServicesNotAvailableException
per indicare che non è in grado di aggiornareProvider
. L'app può quindi rilevare l'eccezione e scegliere un'azione appropriata, ad esempio visualizzare il diagramma di flusso per la risoluzione dei problemi standard.
La
installIfNeededAsync()
si comporta in modo simile, tranne che invece di
generando eccezioni, chiama il metodo di callback appropriato per indicare
un successo o un insuccesso.
Se il fornitore di servizi di sicurezza è già aggiornato, installIfNeeded()
richiede una
trascurabile. Se il metodo
deve installare un nuovo Provider
, l'operazione può richiedere
da 30-50 ms (sui dispositivi più recenti) a 350 ms (sui dispositivi meno recenti)
dispositivi). Per evitare di compromettere l'esperienza utente:
- Chiama
installIfNeeded()
dai thread di networking in background subito quando i thread vengono caricati, anziché attendere il thread per provare a utilizzare la rete. Non c'è alcun problema a chiamare il metodo più volte, poiché restituisce immediatamente se il fornitore di servizi di sicurezza non necessita di aggiornamento. - Chiama il pulsante
più recente del metodo,
installIfNeededAsync()
, se il thread può influire sull'esperienza utente blocco, ad esempio se la chiamata proviene da un'attività nel thread dell'interfaccia utente. Se lo fai, dovrai attendere il completamento dell'operazione prima di tentare qualsiasi comunicazione sicura.ProviderInstaller
chiama il metodoonProviderInstalled()
dell'ascoltatore per segnalare il successo.
Avviso: se ProviderInstaller
non è in grado di installare un Provider
aggiornato, il fornitore di servizi di sicurezza del tuo dispositivo potrebbe essere vulnerabile a exploit noti. La tua app
dovrebbero comportarsi come se tutte le comunicazioni HTTP non fossero criptate.
Una volta aggiornato Provider
, tutte le chiamate a
le API di sicurezza (incluse le API SSL) vengono indirizzate tramite questo.
Tuttavia, questo non si applica a android.net.SSLCertificateSocketFactory
, che rimane vulnerabile a exploit come CVE-2014-0224.
Applica patch in modo sincrono
Il modo più semplice per applicare la patch al provider di sicurezza è chiamare il metodo installIfNeeded()
sincrono.
Questa opzione è appropriata se l'esperienza utente non è interessata dal blocco dei thread
mentre attende il completamento dell'operazione.
Ad esempio, ecco un'implementazione di un worker che aggiorna il provider di sicurezza. Poiché un worker
viene eseguito in background, va bene che il thread si blocchi durante l'attesa
per aggiornare il fornitore di sicurezza. Il worker chiama
Da installIfNeeded()
a
aggiorna il provider di sicurezza. Se il metodo restituisce normalmente,
sa che il fornitore di servizi di sicurezza è aggiornato. Se il metodo genera un'eccezione, il worker può intraprendere l'azione appropriata (ad esempio chiedere all'utente di aggiornare Google Play Services).
Kotlin
/** * Sample patch Worker using {@link ProviderInstaller}. */ class PatchWorker(appContext: Context, workerParams: WorkerParameters): Worker(appContext, workerParams) { override fun doWork(): Result { try { ProviderInstaller.installIfNeeded(context) } catch (e: GooglePlayServicesRepairableException) { // Indicates that Google Play services is out of date, disabled, etc. // Prompt the user to install/update/enable Google Play services. GoogleApiAvailability.getInstance() .showErrorNotification(context, e.connectionStatusCode) // Notify the WorkManager that a soft error occurred. return Result.failure() } catch (e: GooglePlayServicesNotAvailableException) { // Indicates a non-recoverable error; the ProviderInstaller can't // install an up-to-date Provider. // Notify the WorkManager that a hard error occurred. return Result.failure() } // If this is reached, you know that the provider was already up to date // or was successfully updated. return Result.success() } }
Java
/** * Sample patch Worker using {@link ProviderInstaller}. */ public class PatchWorker extends Worker { ... @Override public Result doWork() { try { ProviderInstaller.installIfNeeded(getContext()); } catch (GooglePlayServicesRepairableException e) { // Indicates that Google Play services is out of date, disabled, etc. // Prompt the user to install/update/enable Google Play services. GoogleApiAvailability.getInstance() .showErrorNotification(context, e.connectionStatusCode) // Notify the WorkManager that a soft error occurred. return Result.failure(); } catch (GooglePlayServicesNotAvailableException e) { // Indicates a non-recoverable error; the ProviderInstaller can't // install an up-to-date Provider. // Notify the WorkManager that a hard error occurred. return Result.failure(); } // If this is reached, you know that the provider was already up to date // or was successfully updated. return Result.success(); } }
Esegui il patch in modo asincrono
L'aggiornamento del provider di sicurezza può richiedere fino a 350 ms (su
dispositivi meno recenti). Se esegui l'aggiornamento su un thread che interessa direttamente
esperienza utente, ad esempio il thread dell'interfaccia utente, non vuoi rendere sincrona
per aggiornare il fornitore, dato che può causare l'accesso all'app o al dispositivo
si blocca fino al termine dell'operazione. Utilizza invece il metodo asincrono installIfNeededAsync()
. Questo metodo indica se l'operazione è riuscita o meno mediante la chiamata
i callback.
Ad esempio, di seguito è riportato del codice che aggiorna il provider di sicurezza in un'attività nel thread dell'interfaccia utente. L'attività chiama installIfNeededAsync()
aggiornare il provider e si autodefinisce come listener per l'esito positivo
o notifiche di errore. Se il fornitore di servizi di sicurezza è aggiornato
aggiornate correttamente, l'attività
onProviderInstalled()
e l'attività sa che la comunicazione è sicura. Se il fornitore non può essere aggiornato, viene chiamato il metodo onProviderInstallFailed()
dell'attività, che può intraprendere l'azione appropriata (ad esempio chiedere all'utente di aggiornare Google Play Services).
Kotlin
private const val ERROR_DIALOG_REQUEST_CODE = 1 /** * Sample activity using {@link ProviderInstaller}. */ class MainActivity : Activity(), ProviderInstaller.ProviderInstallListener { private var retryProviderInstall: Boolean = false // Update the security provider when the activity is created. override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ProviderInstaller.installIfNeededAsync(this, this) } /** * This method is only called if the provider is successfully updated * (or is already up to date). */ override fun onProviderInstalled() { // Provider is up to date; app can make secure network calls. } /** * This method is called if updating fails. The error code indicates * whether the error is recoverable. */ override fun onProviderInstallFailed(errorCode: Int, recoveryIntent: Intent) { GoogleApiAvailability.getInstance().apply { if (isUserResolvableError(errorCode)) { // Recoverable error. Show a dialog prompting the user to // install/update/enable Google Play services. showErrorDialogFragment(this@MainActivity, errorCode, ERROR_DIALOG_REQUEST_CODE) { // The user chose not to take the recovery action. onProviderInstallerNotAvailable() } } else { onProviderInstallerNotAvailable() } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == ERROR_DIALOG_REQUEST_CODE) { // Adding a fragment via GoogleApiAvailability.showErrorDialogFragment // before the instance state is restored throws an error. So instead, // set a flag here, which causes the fragment to delay until // onPostResume. retryProviderInstall = true } } /** * On resume, check whether a flag indicates that the provider needs to be * reinstalled. */ override fun onPostResume() { super.onPostResume() if (retryProviderInstall) { // It's safe to retry installation. ProviderInstaller.installIfNeededAsync(this, this) } retryProviderInstall = false } private fun onProviderInstallerNotAvailable() { // This is reached if the provider can't be updated for some reason. // App should consider all HTTP communication to be vulnerable and take // appropriate action. } }
Java
/** * Sample activity using {@link ProviderInstaller}. */ public class MainActivity extends Activity implements ProviderInstaller.ProviderInstallListener { private static final int ERROR_DIALOG_REQUEST_CODE = 1; private boolean retryProviderInstall; // Update the security provider when the activity is created. @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ProviderInstaller.installIfNeededAsync(this, this); } /** * This method is only called if the provider is successfully updated * (or is already up to date). */ @Override protected void onProviderInstalled() { // Provider is up to date; app can make secure network calls. } /** * This method is called if updating fails. The error code indicates * whether the error is recoverable. */ @Override protected void onProviderInstallFailed(int errorCode, Intent recoveryIntent) { GoogleApiAvailability availability = GoogleApiAvailability.getInstance(); if (availability.isUserRecoverableError(errorCode)) { // Recoverable error. Show a dialog prompting the user to // install/update/enable Google Play services. availability.showErrorDialogFragment( this, errorCode, ERROR_DIALOG_REQUEST_CODE, new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { // The user chose not to take the recovery action. onProviderInstallerNotAvailable(); } }); } else { // Google Play services isn't available. onProviderInstallerNotAvailable(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == ERROR_DIALOG_REQUEST_CODE) { // Adding a fragment via GoogleApiAvailability.showErrorDialogFragment // before the instance state is restored throws an error. So instead, // set a flag here, which causes the fragment to delay until // onPostResume. retryProviderInstall = true; } } /** * On resume, check whether a flag indicates that the provider needs to be * reinstalled. */ @Override protected void onPostResume() { super.onPostResume(); if (retryProviderInstall) { // It's safe to retry installation. ProviderInstaller.installIfNeededAsync(this, this); } retryProviderInstall = false; } private void onProviderInstallerNotAvailable() { // This is reached if the provider can't be updated for some reason. // App should consider all HTTP communication to be vulnerable and take // appropriate action. } }