更新你的Security Provider来对抗SSL漏洞利用

    安卓依靠security provider保障网络通信安全。然而有时默认的security provider存在安全漏洞。为了防止这些漏洞被利用,Google Play services 提供了一个自动更新设备的security provider的方法来对抗已知的漏洞。通过调用Google Play services方法,可以确保你的应用运行在可以抵抗已知漏洞的设备上。

    举个例子,OpenSSL的漏洞(CVE-2014-0224)会导致中间人攻击,在通信双方不知情的情况下解密流量。Google Play services 5.0提供了一个补丁,但是必须确保应用安装了这个补丁。通过调用Google Play services方法,可以确保你的应用运行在可抵抗攻击的安全设备上。

    注意:更新设备的security provider不是更新android.net.SSLCertificateSocketFactory.比起使用这个类,我们更鼓励应用开发者使用融入密码学的高级方法。大多数应用可以使用类似,HttpClient,这样的API,而不必去设置TrustManager或者创建一个。

    使用providerinstaller类来更新设备的security provider。你可以通过调用该类的方法installIfNeeded()(或者)来验证security provider是否为最新的(必要的话更新它)。

    • 如果设备的Provider成功更新(或已经是最新的),该方法返回正常。

    • 如果设备的Google Play services 库已经过时了,这个方法抛出googleplayservicesrepairableexception异常表明无法更新Provider。应用程序可以捕获这个异常并向用户弹出合适的对话框提示更新Google Play services。

    • 如果产生了不可恢复的错误,该方法抛出表示它无法更新Provider。应用程序可以捕获异常并选择合适的行动,如显示标准问题解决流程图。

    方法类似,但它不抛出异常,而是通过相应的回调方法,以提示成功或失败。

    • 线程加载后立即在后台网络线程中调用installifneeded,而不是等待线程尝试使用网络。(多次调用该方法没有害处,如果安全提供程序不需要更新它会立即返回。)

    • 如果用户体验会受线程阻塞的影响——比如从UI线程中调用,那么使用调用该方法的异步版本。(当然,如果你要这样做,在尝试任何安全通信之前必须等待操作完成。providerinstaller调用监听者的方法发出成功信号。

    警告:如果providerinstaller无法安装更新Provider,您的设备security provider会容易受到已知漏洞的攻击。你的程序等同于所有HTTP通信未被加密。
    一旦更新,所有安全API(包括SSL API)的调用会经过它(但这并不适用于android.net.sslcertificatesocketfactory,面对这种漏洞仍然是脆弱的)。

    同步修补

    修补security provider最简单的方法就是调用同步方法.如果用户体验不会被线程阻塞影响的话,这种方法很合适。

    更新security provider可能耗费350毫秒(旧设备)。如果在一个会直接影响用户体验的线程中更新,如UI线程,那么你不会希望进行同步更新,因为这可能导致应用程序或设备冻结直到操作完成。因此你应该使用异步方法installifneededasync()。方法通过调用回调函数来反馈其成功或失败。
    例如,下面是一些关于更新security provider在UI线程中的活动的代码。调用installifneededasync()来更新security provider,并指定自己为监听器接收成功或失败的通知。如果security provider是最新的或更新成功,会调用方法,并且知道通信是安全的。如果security provider无法更新,会调用onproviderinstallfailed()方法,并采取适当的行动(如提示用户更新Google Play services)

    1. * Sample activity using {@link ProviderInstaller}.
    2. */
    3. public class MainActivity extends Activity
    4. implements ProviderInstaller.ProviderInstallListener {
    5. private static final int ERROR_DIALOG_REQUEST_CODE = 1;
    6. private boolean mRetryProviderInstall;
    7. //Update the security provider when the activity is created.
    8. @Override
    9. protected void onCreate(Bundle savedInstanceState) {
    10. super.onCreate(savedInstanceState);
    11. ProviderInstaller.installIfNeededAsync(this, this);
    12. }
    13. /**
    14. * This method is only called if the provider is successfully updated
    15. * (or is already up-to-date).
    16. */
    17. @Override
    18. // Provider is up-to-date, app can make secure network calls.
    19. }
    20. /**
    21. * This method is called if updating fails; the error code indicates
    22. */
    23. @Override
    24. protected void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
    25. if (GooglePlayServicesUtil.isUserRecoverableError(errorCode)) {
    26. // Recoverable error. Show a dialog prompting the user to
    27. // install/update/enable Google Play services.
    28. GooglePlayServicesUtil.showErrorDialogFragment(
    29. errorCode,
    30. this,
    31. ERROR_DIALOG_REQUEST_CODE,
    32. new DialogInterface.OnCancelListener() {
    33. @Override
    34. public void onCancel(DialogInterface dialog) {
    35. // The user chose not to take the recovery action
    36. onProviderInstallerNotAvailable();
    37. }
    38. });
    39. } else {
    40. // Google Play services is not available.
    41. onProviderInstallerNotAvailable();
    42. }
    43. }
    44. @Override
    45. Intent data) {
    46. super.onActivityResult(requestCode, resultCode, data);
    47. // Adding a fragment via GooglePlayServicesUtil.showErrorDialogFragment
    48. // before the instance state is restored throws an error. So instead,
    49. // set a flag here, which will cause the fragment to delay until
    50. // onPostResume.
    51. mRetryProviderInstall = true;
    52. }
    53. }
    54. /**
    55. * On resume, check to see if we flagged that we need to reinstall the
    56. * provider.
    57. */
    58. @Override
    59. protected void onPostResume() {
    60. super.onPostResult();
    61. if (mRetryProviderInstall) {
    62. // We can now safely retry installation.
    63. ProviderInstall.installIfNeededAsync(this, this);
    64. }
    65. mRetryProviderInstall = false;
    66. }
    67. private void onProviderInstallerNotAvailable() {
    68. // This is reached if the provider cannot be updated for some reason.
    69. // App should consider all HTTP communication to be vulnerable, and take
    70. // appropriate action.
    71. }