Skip to content

Most visited

Recently visited

navigation

更新您的安全提供程序以防范 SSL 攻击

Android 依靠安全 Provider 提供安全的网络通信。不过,我们会不时地在默认安全提供程序中发现漏洞。为了这些漏洞引发问题,您可以通过 Google Play 服务自动更新设备的安全提供程序,从而防范已知攻击。通过调用 Google Play 服务方法,您的应用可以确保其在具有最新更新的设备上运行以防范已知攻击。

例如,在 OpenSSL (CVE-2014-0224) 中发现了一个漏洞,该漏洞会让应用受到可以解密安全流量的“中间人”攻击,而通信双方对此却毫不知情。Google Play 服务版本 5.0 提供了该漏洞的修复,但应用必须确保已安装此修复。通过使用 Google Play 服务方法,您的应用可以确保其在可以防范该攻击的设备上运行。

注意:更新设备的安全 Provider 不会更新 android.net.SSLCertificateSocketFactory我们建议应用开发者使用高级方法与加密进行交互,而不是使用此类。大多数应用都可以使用 HttpsURLConnection 之类的 API,无需设置自定义 TrustManager 或创建 SSLCertificateSocketFactory

使用 ProviderInstaller 为安全提供程序打补丁

要更新设备的安全提供程序,请使用 ProviderInstaller 类。通过调用该类的 installIfNeeded()(或 installIfNeededAsync())方法,您可以验证安全提供程序是否处于最新状态(如果需要,请对其进行更新)。

当您调用 installIfNeeded() 时,ProviderInstaller 将执行以下操作:

installIfNeededAsync() 方法的行为方式相似,只是不会引发异常,而是调用相应的回调方法以表明更新成功还是失败。

如果 installIfNeeded() 需要安装新的 Provider,所需的时间从 30-50 毫秒(在最新的设备上)到 350 毫秒(在较旧的设备上)不等。如果安全提供程序已经处于最新状态,此方法需要的时间微不足道。为避免影响用户体验,请执行以下操作:

警告:如果 ProviderInstaller 无法安装更新的 Provider,您的设备安全提供程序可能容易受到已知攻击。您的应用应表现为好像所有 HTTP 通信都未加密。

在更新 Provider 后,对安全 API(包括 SSL API)的所有调用均通过它来路由。(不过,这不适用于 android.net.SSLCertificateSocketFactory,其仍然容易受到类似 CVE-2014-0224 的攻击。)

同步打补丁

为安全提供程序打补丁的最简单方法是调用同步方法 installIfNeeded()。如果在等待操作完成时用户体验不会受到线程拦截影响,则该方法非常适用。

例如,下面是一个可以更新安全提供程序的同步适配器的实现。由于同步适配器在后台运行,因此,如果正在等待安全提供程序更新,则线程可以实施拦截。同步适配器将调用 installIfNeeded() 来更新安全提供程序。如果此方法正常返回,则同步适配器知道安全提供程序处于最新状态。如果此方法引发异常,则同步适配器可以进行适当的操作(如提示用户更新 Google Play 服务)。

/**
 * 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.
  }
}

异步打补丁

更新安全提供程序需要 350 毫秒的时间(在较旧的设备上)。如果在直接影响用户体验的线程(如 UI 线程)上进行更新,您一定不希望进行同步调用以更新提供程序,因为这会导致应用或设备冻结,直至操作完成。相反,您应该使用异步方法 installIfNeededAsync()。该方法通过调用回调指示其成功还是失败。

例如,下面是可以在 UI 线程的某个 Activity 中更新安全提供程序的一些代码。此 Activity 会调用 installIfNeededAsync() 来更新提供程序,并将自身指定为接收成功或失败通知的侦听器。如果安全提供程序为最新或已成功更新,将调用此 Activity 的 onProviderInstalled() 方法,且 Activity 知道通信是安全的。如果提供程序无法更新,将调用此 Activity 的 onProviderInstallFailed() 方法,且 Activity 可以进行适当的操作(如提示用户更新 Google Play 服务)。

/**
 * 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.
  }
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)