يعتمد Android على Provider
أمان لتوفير اتصالات آمنة على الشبكة. ومع ذلك، من وقت لآخر،
تم العثور على ثغرات أمنية في مزود الأمان الافتراضي. للحماية من
هذه الثغرات الأمنية، توفّر خدمات
Google Play طريقة لتحديث مقدّم الأمان للجهاز تلقائيًا
للحماية من عمليات الاستيلاء المعروفة. من خلال استدعاء طرق "خدمات Google Play"، يمكنك المساعدة في ضمان
تشغيل تطبيقك على جهاز مزوّد بأحدث التحديثات بهدف
الحماية من عمليات الاستيلاء المعروفة.
على سبيل المثال، تم اكتشاف ثغرة أمنية في مكتبة OpenSSL (CVE-2014-0224) يمكن أن تجعل التطبيقات عرضة لهجوم أثناء المسار يفك تشفير الزيارات الآمنة بدون علم أي من الطرفَين. يقدّم الإصدار 5.0 من "خدمات Google Play" حلًا لهذه المشكلة، ولكن على التطبيقات التحقّق من تثبيت هذا الحل. من باستخدام طرق خدمات Google Play، يمكنك المساعدة في التأكد من أن تطبيقك يعمل على جهاز مؤمن ضد هذا الهجوم.
ملاحظة: عند تحديث أمان الجهاز،
Provider
لا يتم تحديث
android.net.SSLCertificateSocketFactory
،
الذي يبقى عرضة للاختراق. بدلاً من استخدام هذه الفئة المتوقّفة نهائيًا، ننصحك بمطوّري التطبيقات باستخدام methods ذات مستوى عالٍ للتفاعل مع التشفير، مثل
HttpsURLConnection
.
تصحيح مزوّد الأمان باستخدام ProviderInstaller
لتحديث موفّر أمان الجهاز، استخدِم
ProviderInstaller
الصف. يمكنك التحقّق من أنّ موفِّر الأمان محدَّث (وتعديله،
إذا لزم الأمر) من خلال استدعاء
طريقة installIfNeeded()
(أو installIfNeededAsync()
)
في هذه الفئة. يوضّح هذا القسم هذه الخيارات على مستوى عالٍ. تقدم الأقسام التالية
خطوات وأمثلة أكثر تفصيلاً.
عند الاتصال بـ installIfNeeded()
،
ProviderInstaller
بإجراء ما يلي:
- إذا تم بنجاح
تحديث
Provider
للجهاز (أو كان محدّثًا)، تُرجع الطريقة القيمة بدون طرح استثناء. - إذا كانت مكتبة "خدمات Google Play" على الجهاز قديمة، سيتم استخدام الطريقة
الرميات
GooglePlayServicesRepairableException
ويمكن للتطبيق بعد ذلك رصد هذا الاستثناء وعرض مربّع حوار مناسب للمستخدم لتحديث خدمات Google Play - في حالة حدوث خطأ غير قابل للإصلاح، تطرح الطريقة
GooglePlayServicesNotAvailableException
للإشارة إلى أنّه يتعذّر تعديلProvider
. يمكن للتطبيق بعد ذلك رصد الاستثناء واختيار مسارٍ مناسبٍ للعمل، مثل عرض مخطّط تدفق حلّ المشاكل العادي.
تتصرف الطريقة
installIfNeededAsync()
بالطريقة نفسها، إلا أنّها بدلاً من
طرح استثناءات، تستدعي طريقة الاستدعاء المناسبة للإشارة إلى
النجاح أو الفشل.
إذا كان مقدّم خدمة الأمان محدّثًا، يستغرق installIfNeeded()
مدّة زمنية قصيرة. إذا كانت الطريقة
يحتاج إلى تثبيت Provider
جديد، قد يستغرق ذلك
أي وقت من 30 إلى 50 ملي ثانية (على الأجهزة الأحدث) إلى 350 ملي ثانية (على الأجهزة الأقدم
الأجهزة). لتجنب التأثير في تجربة المستخدم:
- استدعاء
installIfNeeded()
من سلاسل المحادثات في الخلفية للاتصال بالشبكة فور تحميل سلاسل المحادثات بدلاً من الانتظار إلى أن تحاول سلسلة المحادثات استخدام الشبكة (لا ضرر في استدعاء الطريقة عدة مرات، لأنّها تُرجع القيمة على الفور إذا كان مقدّم الأمان لا يحتاج إلى التحديث). - استخدِم الإصدار غير المتزامن
من الطريقة،
installIfNeededAsync()
، إذا كان من الممكن أن تتأثر تجربة المستخدم بحظر السلسلة . على سبيل المثال، إذا كان الطلب صادرًا عن نشاط في سلسلة محادثات واجهة المستخدم. (في حال إجراء ذلك، عليك الانتظار إلى حين انتهاء العملية. قبل محاولة إجراء أي اتصالات آمنة. يُطلِق الرمزProviderInstaller
طريقةonProviderInstalled()
لدى المستمع للإشارة إلى النجاح.)
تحذير: إذا كانت
ProviderInstaller
غير قادر على تثبيت Provider
محدث،
قد يكون موفِّر أمان الجهاز عرضة للاختراقات المعروفة. تطبيقك
كما لو كانت جميع اتصالات HTTP غير مشفرة.
بعد تعديل Provider
، يتم توجيه جميع طلبات البيانات من
واجهات برمجة تطبيقات الأمان (بما في ذلك واجهات برمجة تطبيقات طبقة المقابس الآمنة) من خلالها.
(ومع ذلك، لا ينطبق ذلك على android.net.SSLCertificateSocketFactory
، الذي يظلّ معرّضًا ل
الاختراقات مثل
CVE-2014-0224).
التصحيح بشكل متزامن
تعد أبسط طريقة لتصحيح موفر الأمان هي الاتصال
الطريقة installIfNeeded()
.
هذا مناسب إذا لم تتأثّر تجربة المستخدم بحظر سلسلة المحادثات.
أثناء انتظار انتهاء العملية.
على سبيل المثال، في ما يلي عملية تنفيذ عامل تعدّل مقدّم الأمان. بما أنّ مشغّل الخدمة يعمل في الخلفية، لا بأس إذا تم حظر سلسلة المحادثات أثناء الانتظار لتلقّي تحديث من مقدّم الخدمة. يتصل العامل
من installIfNeeded()
إلى
يُرجى تحديث موفِّر الأمان. إذا كانت الطريقة تعمل بشكلٍ طبيعي، يعلم العامل
أنّ مقدّم الخدمة مُحدَّث. إذا طرحت الطريقة استثناءً،
يمكن للعامل اتخاذ الإجراء المناسب (مثل مطالبة المستخدم
تحديث خدمات Google Play).
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(); } }
تطبيق التصحيح بشكل غير متزامن
يمكن أن يستغرق تحديث موفِّر الأمان مدة تصل إلى 350 ملّي ثانية (مفعَّلة).
الأجهزة القديمة). إذا كنت بصدد إجراء التعديل في سلسلة محادثات تؤثر مباشرةً في
تجربة المستخدم، مثل سلسلة محادثات واجهة المستخدم، يجب عدم إجراء مكالمة
متزامنة لتعديل الموفِّر، لأنّ ذلك قد يؤدي إلى
توقُّف التطبيق أو الجهاز عن العمل إلى أن تنتهي العملية. بدلاً من ذلك، استخدِم الأسلوب المتزامن
installIfNeededAsync()
. تشير هذه الطريقة إلى نجاحها أو تعذّرها من خلال استدعاء callbacks.
على سبيل المثال، إليك بعض الرموز البرمجية التي تعدّل مقدّم الأمان في أحد
الأنشطة في سلسلة مهام واجهة المستخدم. يُطلِق النشاط installIfNeededAsync()
لتحديث الموفِّر، ويحدِّد نفسه كمستمع لتلقّي إشعارات بالنجاح
أو التوقّف عن العمل. إذا كان مقدّم الخدمة الأمني محدّثًا أو تم تعديله
بنجاح، يتمّ استدعاء أسلوب
onProviderInstalled()
النشاط، ويعرف النشاط أنّ الاتصال آمن. إذا كانت
تحديث المستخدم، فإن النشاط
onProviderInstallFailed()
ويمكن أن يتخذ النشاط الإجراء المناسب (مثل
مطالبة المستخدم بتحديث خدمات Google Play).
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. } }