From 897cf908c9f1e5f5a2c0b4a3ec72636e64b94eb4 Mon Sep 17 00:00:00 2001 From: Andrew Solovay Date: Wed, 30 Jul 2014 15:00:55 -0700 Subject: docs: Documenting fix for CVE-2014-0224 (SSL vulnerability). Wrote a training page on how to use Google Play services to update a device's security provider, which (int. al.) installs a fix against the recently-discovered SSL CCS injection vulnerability. Doc is staged to: http://asolovay.mtv:9338/training/articles/security-gms-provider.html bug: 16296338 Change-Id: I5b0dd052113998d585a46262b216ef0c1b432362 --- .../training/articles/security-gms-provider.jd | 298 +++++++++++++++++++++ docs/html/training/training_toc.cs | 8 + 2 files changed, 306 insertions(+) create mode 100644 docs/html/training/articles/security-gms-provider.jd (limited to 'docs/html') diff --git a/docs/html/training/articles/security-gms-provider.jd b/docs/html/training/articles/security-gms-provider.jd new file mode 100644 index 0000000..0d3cf1e --- /dev/null +++ b/docs/html/training/articles/security-gms-provider.jd @@ -0,0 +1,298 @@ +page.title=Updating Your Security Provider to Protect Against SSL Exploits +page.tags="network","certificates" + +page.article=true +@jd:body + +
+ +
+ + +

Android relies on a security {@link java.security.Provider Provider} to +provide secure network communications. However, from time to time, +vulnerabilities are found in the default security provider. To protect against +these vulnerabilities, Google Play +services provides a way to automatically update a device's security provider +to protect against known exploits. By calling Google Play services methods, your +app can ensure that it's running on a device that has the latest updates to +protect against known exploits.

+ +

For example, a vulnerability was discovered in OpenSSL +(CVE-2014-0224) +that can leave apps open to a "man-in-the-middle" attack that decrypts +secure traffic without either side knowing. With Google Play services version +5.0, a fix is available, but apps must ensure that this fix is installed. By +using the Google Play services methods, your app can ensure that it's running +on a device that's secured against that attack.

+ +

Caution: Updating a device's security {@link +java.security.Provider Provider} does not update {@link +android.net.SSLCertificateSocketFactory +android.net.SSLCertificateSocketFactory}. Rather than using this class, we +encourage app developers to use high-level methods for interacting with +cryptography. Most apps can use APIs like {@link +javax.net.ssl.HttpsURLConnection}, {@link org.apache.http.client.HttpClient}, +and {@link android.net.http.AndroidHttpClient} without needing to set a custom +{@link javax.net.ssl.TrustManager} or create an {@link +android.net.SSLCertificateSocketFactory}.

+ +

Patching the Security Provider with ProviderInstaller

+ +

To update a device's security provider, use the +{@code ProviderInstaller} +class. You can verify that the security provider is up-to-date (and update it, +if necessary) by calling +that class's {@code installIfNeeded()} +(or {@code installIfNeededAsync()}) +method.

+ +

When you call {@code installIfNeeded()}, the +{@code ProviderInstaller} +does the following:

+ + + +

The +{@code installIfNeededAsync()} +method behaves similarly, except that instead of +throwing exceptions, it calls the appropriate callback method to indicate +success or failure.

+ +

If {@code installIfNeeded()} +needs to install a new {@link java.security.Provider Provider}, this can take +anywhere from 30-50 milliseconds (on more recent devices) to 350 ms (on older +devices). If the security provider is already up-to-date, the method takes a +negligible amount of time. To avoid affecting user experience:

+ + + +

Warning: If the +{@code ProviderInstaller} +is unable to install an updated {@link java.security.Provider Provider}, +your device's security provider might be vulnerable to known exploits. Your app +should behave as if all HTTP communication is unencrypted.

+ +

Once the {@link java.security.Provider Provider} is updated, all calls to +security APIs (including SSL APIs) are routed through it. +(However, this does not apply to {@link android.net.SSLCertificateSocketFactory +android.net.SSLCertificateSocketFactory}, which remains vulnerable to such +exploits as +CVE-2014-0224.)

+ +

Patching Synchronously

+ +

The simplest way to patch the security provider is to call the synchronous +method {@code installIfNeeded()}. +This is appropriate if user experience won't be affected by the thread blocking +while it waits for the operation to finish.

+ +

For example, here's an implementation of a sync adapter that updates the security provider. Since a sync +adapter runs in the background, it's okay if the thread blocks while waiting +for the security provider to be updated. The sync adapter calls +{@code installIfNeeded()} to +update the security provider. If the method returns normally, the sync adapter +knows the security provider is up-to-date. If the method throws an exception, +the sync adapter can take appropriate action (such as prompting the user to +update Google Play services).

+ +
/**
+ * Sample sync adapter using {@link ProviderInstaller}.
+ */
+public class SyncAdapter extends AbstractThreadedSyncAdapter {
+
+  ...
+
+  // This is called each time a sync is attempted; this is okay, since the
+  // overhead is negligible if the security provider is up-to-date.
+  @Override
+  public void onPerformSync(Account account, Bundle extras, String authority,
+      ContentProviderClient provider, SyncResult syncResult) {
+    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.
+      GooglePlayServicesUtil.showErrorNotification(
+          e.getConnectionStatusCode(), getContext());
+
+      // Notify the SyncManager that a soft error occurred.
+      syncResult.stats.numIOExceptions++;
+      return;
+
+    } catch (GooglePlayServicesNotAvailableException e) {
+      // Indicates a non-recoverable error; the ProviderInstaller is not able
+      // to install an up-to-date Provider.
+
+      // Notify the SyncManager that a hard error occurred.
+      syncResult.stats.numAuthExceptions++;
+      return;
+    }
+
+    // If this is reached, you know that the provider was already up-to-date,
+    // or was successfully updated.
+  }
+}
+ +

Patching Asynchronously

+ +

Updating the security provider can take as much as 350 milliseconds (on +older devices). If you're doing the update on a thread that directly affects +user experience, such as the UI thread, you don't want to make a synchronous +call to update the provider, since that can result in the app or device +freezing until the operation finishes. Instead, you should use the asynchronous +method +{@code installIfNeededAsync()}. +That method indicates its success or failure by calling callbacks.

+ +

For example, here's some code that updates the security provider in an +activity in the UI thread. The activity calls {@code installIfNeededAsync()} +to update the provider, and designates itself as the listener to receive success +or failure notifications. If the security provider is up-to-date or is +successfully updated, the activity's +{@code onProviderInstalled()} +method is called, and the activity knows communication is secure. If the +provider cannot be updated, the activity's +{@code onProviderInstallFailed()} +method is called, and the activity can take appropriate action (such as +prompting the user to update Google Play services).

+ +
/**
+ * Sample activity using {@link ProviderInstaller}.
+ */
+public class MainActivity extends Activity
+    implements ProviderInstaller.ProviderInstallListener {
+
+  private static final int ERROR_DIALOG_REQUEST_CODE = 1;
+
+  private boolean mRetryProviderInstall;
+
+  //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) {
+    if (GooglePlayServicesUtil.isUserRecoverableError(errorCode)) {
+      // Recoverable error. Show a dialog prompting the user to
+      // install/update/enable Google Play services.
+      GooglePlayServicesUtil.showErrorDialogFragment(
+          errorCode,
+          this,
+          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 is not 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 GooglePlayServicesUtil.showErrorDialogFragment
+      // before the instance state is restored throws an error. So instead,
+      // set a flag here, which will cause the fragment to delay until
+      // onPostResume.
+      mRetryProviderInstall = true;
+    }
+  }
+
+  /**
+   * On resume, check to see if we flagged that we need to reinstall the
+   * provider.
+   */
+  @Override
+  protected void onPostResume() {
+    super.onPostResult();
+    if (mRetryProviderInstall) {
+      // We can now safely retry installation.
+      ProviderInstall.installIfNeededAsync(this, this);
+    }
+    mRetryProviderInstall = false;
+  }
+
+  private void onProviderInstallerNotAvailable() {
+    // This is reached if the provider cannot be updated for some reason.
+    // App should consider all HTTP communication to be vulnerable, and take
+    // appropriate action.
+  }
+}
+
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 4407d30..4dd57ab 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -1487,6 +1487,14 @@ results." >Security with HTTPS and SSL +
  • + Updating Your Security Provider to Protect Against SSL Exploits +
  • +