SSL শোষণ থেকে রক্ষা করতে আপনার নিরাপত্তা প্রদানকারীকে আপডেট করুন

নিরাপদ নেটওয়ার্ক যোগাযোগ প্রদানের জন্য 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 ইনস্টল করার প্রয়োজন হয়, তাহলে এটি 30-50 ms (আরও সাম্প্রতিক ডিভাইসে) থেকে 350 ms (পুরানো ডিভাইসগুলিতে) যে কোনো জায়গায় নিতে পারে। ব্যবহারকারীর অভিজ্ঞতা প্রভাবিত এড়াতে:

  • নেটওয়ার্ক ব্যবহার করার চেষ্টা করার জন্য থ্রেডের জন্য অপেক্ষা করার পরিবর্তে থ্রেডগুলি লোড হওয়ার সাথে সাথে পটভূমি নেটওয়ার্কিং থ্রেডগুলি থেকে installIfNeeded() কল করুন। (পদ্ধতিটি একাধিকবার কল করার কোন ক্ষতি নেই, যেহেতু নিরাপত্তা প্রদানকারীর আপডেট করার প্রয়োজন না হলে এটি অবিলম্বে ফিরে আসে।)
  • পদ্ধতির অ্যাসিঙ্ক্রোনাস সংস্করণে কল করুন, installIfNeededAsync() , যদি ব্যবহারকারীর অভিজ্ঞতা থ্রেড ব্লকিং দ্বারা প্রভাবিত হতে পারে—উদাহরণস্বরূপ, যদি কলটি UI থ্রেডের কোনো কার্যকলাপ থেকে হয়। (যদি আপনি এটি করেন, আপনি কোনো সুরক্ষিত যোগাযোগের চেষ্টা করার আগে আপনাকে অপারেশনটি শেষ হওয়ার জন্য অপেক্ষা করতে হবে। ProviderInstaller আপনার শ্রোতার onProviderInstalled() পদ্ধতিকে সাফল্যের সংকেত দেয়।)

সতর্কতা: যদি ProviderInstaller একটি আপডেট করা Provider ইনস্টল করতে অক্ষম হয়, তাহলে আপনার ডিভাইসের নিরাপত্তা প্রদানকারী পরিচিত শোষণের জন্য দুর্বল হতে পারে। আপনার অ্যাপটি এমন আচরণ করা উচিত যেন সমস্ত HTTP যোগাযোগ এনক্রিপ্ট করা নেই।

একবার Provider আপডেট হয়ে গেলে, নিরাপত্তা API-তে (SSL API সহ) সমস্ত কল এর মাধ্যমে রুট করা হয়। (তবে, এটি android.net.SSLCertificateSocketFactory এর ক্ষেত্রে প্রযোজ্য নয়, যা CVE-2014-0224- এর মতো শোষণের জন্য ঝুঁকিপূর্ণ থাকে।)

সিঙ্ক্রোনাসভাবে প্যাচ করুন

নিরাপত্তা প্রদানকারীকে প্যাচ করার সবচেয়ে সহজ উপায় হল সিঙ্ক্রোনাস পদ্ধতি installIfNeeded() কল করা। এটি উপযুক্ত যদি ব্যবহারকারীর অভিজ্ঞতা থ্রেড ব্লকিং দ্বারা প্রভাবিত না হয় যখন এটি অপারেশন শেষ হওয়ার জন্য অপেক্ষা করে।

উদাহরণস্বরূপ, এখানে একজন কর্মীর বাস্তবায়ন রয়েছে যা নিরাপত্তা প্রদানকারীকে আপডেট করে। যেহেতু একজন কর্মী ব্যাকগ্রাউন্ডে চলে, তাই নিরাপত্তা প্রদানকারীর আপডেট হওয়ার জন্য অপেক্ষা করার সময় থ্রেড ব্লক হয়ে গেলে ঠিক আছে। নিরাপত্তা প্রদানকারী আপডেট করার জন্য কর্মী installIfNeeded() কল করে। যদি পদ্ধতিটি স্বাভাবিকভাবে ফিরে আসে, তাহলে কর্মী জানেন যে নিরাপত্তা প্রদানকারী আপ টু ডেট। যদি পদ্ধতিটি একটি ব্যতিক্রম ছুঁড়ে দেয়, কর্মী যথাযথ ব্যবস্থা নিতে পারে (যেমন ব্যবহারকারীকে Google Play পরিষেবাগুলি আপডেট করার জন্য অনুরোধ করা)।

কোটলিন

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

অ্যাসিঙ্ক্রোনাসভাবে প্যাচ করুন

নিরাপত্তা প্রদানকারী আপডেট করতে 350 ms (পুরানো ডিভাইসে) সময় লাগতে পারে। আপনি যদি এমন একটি থ্রেডের আপডেট করছেন যা সরাসরি ব্যবহারকারীর অভিজ্ঞতাকে প্রভাবিত করে, যেমন UI থ্রেড, আপনি প্রদানকারীকে আপডেট করার জন্য একটি সিঙ্ক্রোনাস কল করতে চান না, কারণ এর ফলে অপারেশন না হওয়া পর্যন্ত অ্যাপ বা ডিভাইস ফ্রিজ হয়ে যেতে পারে। সমাপ্তি পরিবর্তে, অসিঙ্ক্রোনাস পদ্ধতি ব্যবহার করুন installIfNeededAsync() । সেই পদ্ধতি কলব্যাক কল করে তার সাফল্য বা ব্যর্থতা নির্দেশ করে।

উদাহরণস্বরূপ, এখানে কিছু কোড রয়েছে যা UI থ্রেডের একটি কার্যকলাপে নিরাপত্তা প্রদানকারীকে আপডেট করে। ক্রিয়াকলাপটি প্রদানকারীকে আপডেট করার জন্য installIfNeededAsync() কল করে এবং সাফল্য বা ব্যর্থতার বিজ্ঞপ্তিগুলি পাওয়ার জন্য নিজেকে শ্রোতা হিসাবে মনোনীত করে। যদি নিরাপত্তা প্রদানকারী আপ টু ডেট থাকে বা সফলভাবে আপডেট করা হয়, তাহলে কার্যকলাপের onProviderInstalled() পদ্ধতি বলা হয়, এবং কার্যকলাপ জানে যে যোগাযোগ নিরাপদ। যদি প্রদানকারীকে আপডেট করা না যায়, তাহলে অ্যাক্টিভিটির onProviderInstallFailed() পদ্ধতি বলা হয় এবং অ্যাক্টিভিটি যথাযথ ব্যবস্থা নিতে পারে (যেমন ব্যবহারকারীকে Google Play পরিষেবা আপডেট করার জন্য অনুরোধ করা)।

কোটলিন

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