Android, সুরক্ষিত নেটওয়ার্ক কমিউনিকেশন প্রদান করতে
নিরাপত্তা Provider-এর উপর নির্ভর করে। তবে, মাঝে মাঝে,
ডিফল্ট নিরাপত্তা প্রদানকারীর মধ্যে দুর্বলতা খুঁজে পাওয়া যায়। এই ধরনের দুর্বলতা থেকে
সুরক্ষা দিতে, Google Play
পরিষেবা ডিভাইসের নিরাপত্তা প্রদানকারীকে অটোমেটিক আপডেট করার সুবিধা প্রদান করে
যাতে পরিচিত এক্সপ্লয়েট থেকে সুরক্ষিত থাকা যায়। Google Play পরিষেবার পদ্ধতি কল করার মাধ্যমে, আপনি নিশ্চিত করতে সাহায্য করতে পারেন
যে আপনার অ্যাপটি এমন একটি ডিভাইসে চলছে যাতে পরিচিত এক্সপ্লয়েট থেকে
সুরক্ষা পেতে লেটেস্ট আপডেট রয়েছে।
যেমন, OpenSSL-এ একটি দুর্বলতা খুঁজে পাওয়া গেছে (CVE-2014-0224) যা অ্যাপকে অন-পাথ অ্যাটাকের জন্য উন্মুক্ত করে দেয় এবং এর ফলে কোনও পক্ষকে না জানিয়েই সুরক্ষিত ট্রাফিক ডিক্রিপ্ট করা যায়। Google Play পরিষেবার 5.0 ভার্সন একটি সমাধান অফার করে, তবে অ্যাপগুলিকে অবশ্যই চেক করতে হবে যে এই সমাধানটি ইনস্টল করা আছে কিনা। Google Play পরিষেবা পদ্ধতি ব্যবহার করে, আপনি এটি নিশ্চিত করতে সাহায্য করতে পারেন যে আপনার অ্যাপটি এমন একটি ডিভাইসে চলছে যা সেই আক্রমণের বিরুদ্ধে সুরক্ষিত।
সতর্কতা: কোনও ডিভাইসের নিরাপত্তা আপডেট করলে
Provider android.net.SSLCertificateSocketFactory আপডেট হয় না,
যা অসুরক্ষিত থেকে যায়। এই বাতিল করা ক্লাস ব্যবহার করার পরিবর্তে, আমরা অ্যাপ ডেভেলপারদের
ক্রিপ্টোগ্রাফির সাথে ইন্টার্যাক্ট করার জন্য হাই-লেভেল পদ্ধতি ব্যবহার করতে উৎসাহিত করি, যেমন
HttpsURLConnection।
ProviderInstaller ব্যবহার করে নিরাপত্তা প্রদানকারীকে প্যাচ করুন
ডিভাইসের নিরাপত্তা প্রদানকারী আপডেট করতে, ProviderInstaller
ক্লাস ব্যবহার করুন। আপনি যাচাই করতে পারেন যে নিরাপত্তা প্রদানকারী আপ-টু-ডেট আছে কিনা (এবং এটি আপডেট করুন,
প্রয়োজন হলে)
সেই ক্লাসের installIfNeeded()
(বা installIfNeededAsync())
পদ্ধতি কল করে। এই বিভাগে এইসব বিকল্পের ব্যাপারে বিস্তারিত তথ্য দেওয়া হয়েছে। নিচের বিভাগগুলিতে আরও বিস্তারিত ধাপ ও উদাহরণ
দেওয়া আছে।
আপনি installIfNeeded()-কে কল করলে, ProviderInstaller
নিম্নলিখিত কাজগুলি করে:
- ডিভাইসের
Providerসফলভাবে আপডেট করা হলে (অথবা আগে থেকেই আপ-টু-ডেট থাকলে), পদ্ধতিটি কোনও ব্যতিক্রম না দেখিয়েই ফিরে আসে। - ডিভাইসের Google Play পরিষেবা লাইব্রেরি পুরনো হয়ে গেলে, পদ্ধতিটি
GooglePlayServicesRepairableExceptionথ্রো করে। অ্যাপটি তখন এই ব্যতিক্রমটি ধরতে পারে এবং ব্যবহারকারীকে Google Play পরিষেবা আপডেট করার জন্য একটি উপযুক্ত ডায়ালগ বক্স দেখাতে পারে। - যদি এমন কোনও সমস্যা হয় যা সমাধান করা যাবে না, তাহলে পদ্ধতিটি
GooglePlayServicesNotAvailableExceptionথ্রো করে যাতে বোঝা যায় যে এটিProviderআপডেট করতে পারছে না। অ্যাপটি তখন ব্যতিক্রমটি ধরতে পারে এবং উপযুক্ত পদক্ষেপ বেছে নিতে পারে, যেমন স্ট্যান্ডার্ড ফিক্স-ইট ফ্লো ডায়াগ্রাম দেখানো।
installIfNeededAsync()
পদ্ধতিটি একইভাবে কাজ করে, তবে ব্যতিক্রম দেখানোর পরিবর্তে, এটি সাফল্য বা ব্যর্থতা নির্দেশ করার জন্য উপযুক্ত কলব্যাক পদ্ধতিকে কল করে।
নিরাপত্তা প্রদানকারী আগে থেকেই আপ-টু-ডেট থাকলে, installIfNeeded()-এর জন্য
খুবই কম সময় লাগে। পদ্ধতিটি
নতুন Provider ইনস্টল করতে চাইলে, এতে ৩০-৫০ মিলি-সেকেন্ড (আরও সাম্প্রতিক ডিভাইসে) থেকে ৩৫০ মিলি-সেকেন্ড (পুরনো ডিভাইসে)
পর্যন্ত সময় লাগতে
পারে। ব্যবহারকারীর অভিজ্ঞতা যাতে প্রভাবিত না হয়, তার জন্য:
- থ্রেড নেটওয়ার্ক ব্যবহার করার চেষ্টা করার জন্য অপেক্ষা না করে,
থ্রেড লোড হওয়ার সাথে সাথে ব্যাকগ্রাউন্ড নেটওয়ার্কিং থ্রেড থেকে
installIfNeeded()কল করুন। (এই পদ্ধতিটি একাধিকবার কল করলে কোনও ক্ষতি নেই, কারণ নিরাপত্তা প্রদানকারীকে আপডেট করার প্রয়োজন না হলে এটি অবিলম্বে ফিরে আসে।) - থ্রেড ব্লক করার কারণে ব্যবহারকারীর অভিজ্ঞতা প্রভাবিত হলে,
পদ্ধতির অ্যাসিঙ্ক্রোনাস ভার্সন,
installIfNeededAsync()-এ কল করুন—যেমন, UI থ্রেডে কোনও অ্যাক্টিভিটি থেকে কল করা হলে। (আপনি এটি করলে, কোনও সুরক্ষিত যোগাযোগ করার চেষ্টা করার আগে আপনাকে অপারেশনটি শেষ হওয়ার জন্য অপেক্ষা করতে হবে।ProviderInstallerআপনার লিসনারেরonProviderInstalled()মেথডকে কল করে সফলতার সিগন্যাল দেয়।)
সতর্কতা:
ProviderInstaller
যদি আপডেট করা Provider ইনস্টল করতে না পারে,
তাহলে আপনার ডিভাইসের নিরাপত্তা প্রদানকারী পরিচিত এক্সপ্লয়েটের ক্ষেত্রে অসুরক্ষিত হতে পারে। আপনার অ্যাপ
এমনভাবে কাজ করা উচিত যাতে মনে হয় সব HTTP কমিউনিকেশন এনক্রিপ্ট করা নেই।
Provider আপডেট করা হয়ে গেলে, নিরাপত্তা API-তে (SSL API সহ)
করা সব কল এর মাধ্যমে রুট করা হয়।
(তবে, এটি android.net.SSLCertificateSocketFactory-এর ক্ষেত্রে প্রযোজ্য নয়, যা
CVE-2014-0224-এর মতো
এক্সপ্লয়েটের ক্ষেত্রে অসুরক্ষিত থাকে।)
সিঙ্ক্রোনাসভাবে প্যাচ করুন
নিরাপত্তা প্রদানকারীকে প্যাচ করার সবচেয়ে সহজ উপায় হল সিঙ্ক্রোনাস
পদ্ধতি installIfNeeded() কল করা।
অপারেশন শেষ হওয়ার জন্য অপেক্ষা করার সময় থ্রেড ব্লক করার ফলে ব্যবহারকারীর অভিজ্ঞতা প্রভাবিত না হলে
এটি উপযুক্ত।
যেমন, এখানে নিরাপত্তা প্রদানকারীকে আপডেট করে এমন একটি worker-এর প্রয়োগ দেখানো হয়েছে। যেহেতু ব্যাকগ্রাউন্ডে
ওয়ার্কার রান করে, তাই নিরাপত্তা প্রদানকারী আপডেট হওয়ার জন্য অপেক্ষা করার সময়
থ্রেড ব্লক হলে কোনও সমস্যা নেই। নিরাপত্তা প্রদানকারীকে আপডেট করতে
কর্মী 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() } }
জাভা
/** * 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(); } }
অ্যাসিঙ্ক্রোনাসলি প্যাচ করুন
নিরাপত্তা প্রদানকারী আপডেট করতে ৩৫০ মিলি সেকেন্ড (পুরনো ডিভাইসে) পর্যন্ত সময় লাগতে পারে। আপনি যদি এমন কোনও থ্রেডে আপডেট করেন যা সরাসরি ব্যবহারকারীর অভিজ্ঞতাকে প্রভাবিত করে, যেমন UI থ্রেড, তাহলে আপনি প্রোভাইডারকে আপডেট করার জন্য সিঙ্ক্রোনাস কল করতে চাইবেন না, কারণ এর ফলে অপারেশন শেষ না হওয়া পর্যন্ত অ্যাপ বা ডিভাইস ফ্রিজ হয়ে যেতে পারে। পরিবর্তে, অ্যাসিঙ্ক্রোনাস
পদ্ধতি installIfNeededAsync() ব্যবহার করুন। কলব্যাক কল করার মাধ্যমে সেই পদ্ধতিটি সফল হয়েছে নাকি ব্যর্থ হয়েছে তা বোঝা যায়।
যেমন, এখানে কিছু কোড দেওয়া হল যা UI থ্রেডে অ্যাক্টিভিটিতে নিরাপত্তা প্রদানকারীকে আপডেট করে। অ্যাক্টিভিটি 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. } }
জাভা
/** * 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. } }