به روز رسانی Security Providers

به روز رسانی Security Providers برای جلوگیری از SSL Exploit

سیستم عامل اندروید برای تامین امنیت در ارتباط های روی شبکه، از Security Providers استفاده میکند. اما در طول زمان خطرات امنیتی مختلفی در همین Provider پیشفرض پیدا میشود. برای اینکه بتوانید اپلیکیشن خودتان را در برابر این خطرات محافظت کنید، سرویس های گوگل پلی به شما اجازه میدهند به صورت اتوماتیک Security Providers های اپلیکیشن ها را آپدیت کنید. اپلیکیشن شما میتوانید با فراخوانی متدهایی که در Google Play Services موجود است مطمئن شود که دستگاه کاربر دارای اخرین به روز رسانی Security Providers میباشد، تا از خطرات احتمالی آینده جلوگیری کند.

به روز رسانی Security Providers در اندروید

در این نوشته از برنامه چی میخواهیم روش به روز رسانی Security Providers در اندروید را بررسی کنیم. در این مقاله درباره به روز رسانی Security Providers، پچ کردن آن و متد ها و کلاس های مختلفی که برای این کار وجود دارند صحبت کردیم. فهرست تیتر های این مقاله را میتوانید در ادامه مشاهده کنید.


یک مثال از SSL Exploit

به روز رسانی Security Providers در اندروید برای جلوگیری از ssl exploit

به عنوان مثال، یک آسیب پذیری امنیتی در OpenSSL کشف شد (CVE-2014-0224) که میتوانست اپلیکیشن ها را در معرض حمله man-in-the-middle قرار بدهد و بدون اینکه طرفین رابطه را شناسایی کند، ترافیک امن را کدگشایی میکرد. بعد از گوگل پلی سرویس ورژن 5.0 یک راه حل ارائه شد، اما اپلیکیشن ها باید مطمئن میشدند که این راه حل روی آنها نصب شده است. با استفاده از متدهای Google Play Service میتوانید مطمئن شوید اپلیکیشن شما روی یک دستگاه ایمن نصب شده و اجرا میشود.

هشدار: به روز رسانی Provider یک دستگاه، android.net.SSLCertificateSocketFactory آن را آپدیت نمیکند. به جای استفاده از این کلاس، توصیه میشود از متدهای سطح بالا برای تعامل با کریپتوگرافی استفاده کنید. بسیاری از اپلیکیشن ها میتوانند از API هایی مانند HttpsURLConnection استفاده کنند بدون اینکه نیازی به تنظیم یک TrustManger شخصی یا ساخت یک SSLCertificateSocketFactory باشد.


Patch کردن Security Providers با ProviderInstaller

برای به روز رسانی Security Providers یک دستگاه، میتوانیم از کلاس ProviderInstaller استفاده کنیم. این کلاس به شما اجازه میدهد بررسی کنید آیا Security Providers به روز رسانی شده است یا نه (اگر لازم بود میتوانید آن را آپدیت کنید). برای این کار باید متد installIfNeeded یا installIfNeededAsync از همین کلاس را صدا بزنید.

به روز رسانی Security Providers

وقتی که متد installIfNeeded را صدا میزنید، کلاس ProviderInstaller این کارها را انجام میدهد:

  • اگر Provider دستگاه با موفقیت آپدیت شود، یا از قبل آپدیت شده باشد، متد بصورت عادی متوقف خواهد شد.
  • اگر کتابخانه گوگل پلی سرویس دستگاه نسخه قدیمی باشد، این متد خطای GooglePlayServicesRepairableException را پرتاب میکند. اپلیکیشن میتواند این خطا را دریافت کند و به کاربر یک دیالوگ باکس برای آپدیت کردن سرویس های گوگل پلی نشان بدهد.
  • اگر یک خطای غیر قابل برگشت اتفاق بیفتد، متد خطای GooglePlayServicesNotAvailableException را پرتاب میکند. این خطا نشان میدهد که متد نمیتواند Provider را به روز رسانی کند. اپلیکیشن میتواند این خطا را دریافت کند و عملیات های مناسب خودش را انجام بدهد (مثلا نشان دادن یک دیاگرام استاندارد برای حل مشکل).

متد installIfNeededAsync رفتاری شبیه به متد قبلی دارد با این تفاوت که به جای پرتاب Exception های مختلف، با صدا زدن Callback های مناسب موفقیت یا شکست عملیات را اعلام میکند.

اگر متد installIfNeeded نیاز به نصب یک Provider جدید داشته باشد، ممکن است به زمانی بین 30 الی 50 میلی ثانیه و در دستگاه های قدیمی تر تا 350 میلی ثانیه داشته باشد. اگر Security Provider از قبل آپدیت شده باشد، متد نیاز به زمان بسیار کمی خواهد داشت. برای اینکه تجربه کاربری تحت تاثیر قرار نگیرد، میتوانیم کارهای زیر را انجام بدهیم:

  • متد installIfNeeded را در Thread های پس زمینه مربوط به شبکه، قبل از اینکه این Thread ها سعی کنند ارتباط با اینترنت را بررسی کنند صدا بزنید. (اینکه چندبار این متد را صدا بزنید اصلا اشکال ندارد. وقتی که نیازی به آپدیت نباشد، اجرای متد سریعا تمام میشود).
  • اگر احتمال میدهید که Thread رابط کاربری با اجرای این متد بلاک میشود، یا اگر میخواهید این متد را از اکتیویتی یا UI Thread صدا بزنید، بهتر است از نسخه غیر همزمان متد، یعنی installIfNeededAsync، استفاده کنید. (البته این نکته رو فراموش نکنید که اگر این متد را به کار ببرید، باید قبل از اینکه هر ارتباطی با شبکه برقرار کنید، منتظر بمانید همه کارهای این متد تمام بشوند. کلاس ProviderInstaller یک Listener به نام onProviderInstalled را بعد از اینکه کارهای این متد تمام شد، صدا میزند).
به روز رسانی Security Providers

هشدار: اگر ProviderInstaller نتواند یک Provider به روز را نصب کند، Security Providers دستگاه شما میتواند در معرض خطرات امنیتی قرار بگیرد. در این شرایط، اپلیکیشن شما باید طوری رفتار کند انگار که همه ارتباطات از طریق پروتکل غیر ایمن HTTP انجام میشود.

وقتی که Security Providers دستگاه شما آپدیت شد، همه ارتباطات با API های امنیتی (حتی SSL API ها) از طریق آن عبور خواهند کرد. (البته این مسئله برای android.net.SSLCertificateSocketFactory اتفاق نمی افتد. یعنی هنوز میتواند در معرض اکسپلویت هایی مانند CVE-2014-0224 قرار بگیرد).


Patch کردن بصورت همزمان

راحت ترین راه برای Patch کردن Security Providers، صدا زدن متد installIfNeeded است که بصورت همزمان اجرا میشود. استفاده از این متد فقط به شرطی که Thread رابط کاربری را بلاک نکند و باعث ضعیف شدن تجربه کاربری نشود، راهکار مناسبی است.

برای مثال، کدهای زیر نمونه ای از یک Sync Adapter است که وظیفه آن به روز رسانی Security Providers میباشد. از آنجایی که Sync Adapter در بکگراند اجرا میشود، مشکلی برای Thread به وجود نمی آید و میتواند برای به روز رسانی Security Providers منتظر بماند. این آداپتر متد installIfNeeded را برای به روز رسانی Security Providers را صدا میزند. اگر متد بصورت طبیعی Return شود، آداپتر میفهمد که Security Providers به درستی آپدیت شده است. اگر یک Exception پرتاب شود، Sync Adapter میتواند عملیات مناسب برای آن را انجام بدهد (مثلا از کاربر برای آپدیت کردن گوگل پلی سرویس اجازه بگیرد).

/**
 * 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.
      GoogleApiAvailability.getInstance()
              .showErrorNotification(context, e.connectionStatusCode)

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

Patch کردن بصورت غیر همزمان

به روز رسانی Security Providers میتواند تا 350 میلی ثانیه روی دستگاه های قدیمی طول بکشد. اگر میخواهید عملیات به روز رسانی را روی یک Thread انجام بدهید که روی تجربه کاربری تاثیر مستقیم دارد، مانند UI Thread، نباید آپدیت کردن را بصورت همزمان انجام بدهید. زیرا این کار باعث فریز شدن اپلکیشن و آسیب جدی به تجربه کاربر میشود. به جای این کار باید از متد غیر همزمان installIfNeededAsync استفاده کنید. این متد موفقیت و شکست خودش را با Callback اطلاع میدهد.

برای مثال، کدهای زیر عملیات به روز رسانی Security Providers درون یک اکتیوتی را نشان میدهد. اکتیویتی متد installIfNeededAsync را برای به روز رسانی Provider صدا میزند و خودش را به عنوان یک Listener برای شکست یا موفقیت عملیات این متد تعیین میکند. اگر Provider با موفقیت آپدیت شود یا از قبل به روز بوده باشد، متد onProviderInstalled در اکتیویتی صدا زده میشود و بعد از آن اکتیویتی مطمئن است که ارتباطات ایمن هستند.

اگر Provider نتواند به روز رسانی شود، متد onProviderInstallFailed در اکتیویتی صدا زده میشود و اکتیویتی میتواند کارهای مناسب برای این وضعیت را انجام بدهد (مثلا از کاربر برای به روز رسانی گوگل پلی سرویس اجازه بگیرد).

/**
<ul>
 	<li>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);
}

/**</li>
 	<li>This method is only called if the provider is successfully updated</li>
 	<li>(or is already up-to-date).
*/
@Override
protected void onProviderInstalled() {
// Provider is up-to-date, app can make secure network calls.
}

/**</li>
 	<li>This method is called if updating fails; the error code indicates</li>
 	<li>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 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 GoogleApiAvailability.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.
retryProviderInstall = true;
}
}

/**</li>
 	<li>On resume, check to see if we flagged that we need to reinstall the</li>
 	<li>provider.
*/
@Override
protected void onPostResume() {
super.onPostResume();
if (retryProviderInstall) {
// We can now safely retry installation.
ProviderInstaller.installIfNeededAsync(this, this);
}
retryProviderInstall = 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.
}
}

سوالی دارید؟

در این مقاله درباره به روز رسانی Security Providers برای جلوگیری از SSL Exploit صحبت کردیم. اگر هنوز سوالی در ذهن شما وجود دارد یا قسمتی از نوشته به توضیحات بیشتر نیاز دارد، میتوانید آن را در قسمت نظرات (همین پایین) بپرسید و این مقاله آموزشی را کامل تر کنید.


منابع بیشتر برای مطالعه

میتوانید از منابع زیر برای مطالعه بیشتر درباره به روز رسانی Security Providers و جلوگیری از SSL Exploit استفاده کنید:

دیدگاه‌ خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

اسکرول به بالا