تأكيد آمن لعمليات المُستخدِم على Android

لمساعدتك في تأكيد نوايا المستخدمين عند بدء معاملة حساسة، مثل إجراء دفعة، تتيح لك الأجهزة المتوافقة التي تعمل بالإصدار 9 من Android (المستوى 28 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث استخدام ميزة "التأكيد المحمي" من Android. عند استخدام مسار العمل هذا، يعرض تطبيقك طلبًا للمستخدم يطلب منه الموافقة على بيان قصير يؤكد رغبته في إكمال المعاملة الحسّاسة.

إذا وافق المستخدم على البيان، يمكن لتطبيقك استخدام مفتاح من "مفتاح Android" لتوقيع الرسالة المعروضة في مربّع الحوار. يشير التوقيع، بثقة عالية جدًا، إلى أنّ المستخدم قد اطّلَع على البيان ووافق عليه.

تحذير: لا يوفّر ميزة "التأكيد المحمي" في Android قناة معلومات آمنة للمستخدم. لا يمكن لتطبيقك تقديم أي ضمانات سرية تتجاوز تلك التي يوفّرها نظام التشغيل Android. على سبيل المثال، لا تستخدِم سير العمل هذا لعرض معلومات حسّاسة لن تعرضها عادةً على جهاز المستخدم.

بعد تأكيد المستخدم للرسالة، يتم ضمان سلامتها، ولكن يجب أن يستخدم تطبيقك ميزة تشفير البيانات أثناء النقل لحماية سرية الرسالة الموقَّعة.

لتقديم تأكيد عالي الأمان للمستخدم في تطبيقك، أكمِل الخطوات التالية:

  1. أنشئ مفتاح توقيع غير متماثل باستخدام فئة KeyGenParameterSpec.Builder. عند إنشاء المفتاح، مرِّر true إلى setUserConfirmationRequired(). اتصل أيضًا بـ setAttestationChallenge()، مع ضبط قيمة طلب مناسبة يوفّرها الطرف الموثوق به.

  2. سجِّل المفتاح الذي تم إنشاؤه حديثًا وشهادة إثبات ملكية المفتاح مع الطرف المعنيّ بالاعتماد.

  3. أرسِل تفاصيل المعاملة إلى خادمك واطلب منه إنشاء ملف ملف بيانات إضافية وعرضه. قد تتضمّن البيانات الإضافية data التي يجب تأكيدها أو نصائح التحليل، مثل لغة سلسلة الطلب.

    لتنفيذ أكثر أمانًا، يجب أن يحتوي BLOB على رمز مفتاح تشفير عشوائي للحماية من هجمات إعادة التشغيل وإزالة الغموض عن المعاملات.

  4. يمكنك إعداد العنصر ConfirmationCallback الذي يُعلم تطبيقك عندما يقبل المستخدم الطلب المعروض في مربع حوار التأكيد:

    Kotlin

    class MyConfirmationCallback : ConfirmationCallback() {
    
          override fun onConfirmed(dataThatWasConfirmed: ByteArray?) {
              super.onConfirmed(dataThatWasConfirmed)
              // Sign dataThatWasConfirmed using your generated signing key.
              // By completing this process, you generate a signed statement.
          }
    
          override fun onDismissed() {
              super.onDismissed()
              // Handle case where user declined the prompt in the
              // confirmation dialog.
          }
    
          override fun onCanceled() {
              super.onCanceled()
              // Handle case where your app closed the dialog before the user
              // responded to the prompt.
          }
    
          override fun onError(e: Exception?) {
              super.onError(e)
              // Handle the exception that the callback captured.
          }
      }
    

    Java

    public class MyConfirmationCallback extends ConfirmationCallback {
    
      @Override
      public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {
          super.onConfirmed(dataThatWasConfirmed);
          // Sign dataThatWasConfirmed using your generated signing key.
          // By completing this process, you generate a signed statement.
      }
    
      @Override
      public void onDismissed() {
          super.onDismissed();
          // Handle case where user declined the prompt in the
          // confirmation dialog.
      }
    
      @Override
      public void onCanceled() {
          super.onCanceled();
          // Handle case where your app closed the dialog before the user
          // responded to the prompt.
      }
    
      @Override
      public void onError(Throwable e) {
          super.onError(e);
          // Handle the exception that the callback captured.
      }
    }
    

    إذا وافق المستخدم على مربّع الحوار، يتم استدعاء onConfirmed(). dataThatWasConfirmed BLOB هي بنية بيانات CBOR تحتوي، من بين التفاصيل الأخرى، على نص الطلب الذي رآه المستخدم بالإضافة إلى data الإضافية التي تم تمريرها إلى ConfirmationPrompt أداة الإنشاء. استخدِم المفتاح الذي تم إنشاؤه سابقًا لتوقيع ملف BLOB الذي يضمّ dataThatWasConfirmed، ثم أعِد إرسال ملف BLOB هذا إلى الطرف الموثوق به مع التوقيع وتفاصيل المعاملة.

    للاستفادة إلى أقصى حد من ضمان الأمان الذي يوفّره "تأكيد Android المحمي"، على الجهة المُعتمَدة اتّباع الخطوات التالية عند تلقّي رسالة موقَّعة:

    1. تحقَّق من التوقيع على الرسالة بالإضافة إلى سلسلة شهادات التحقّق لمفتاح التوقيع.
    2. تأكَّد من أنّ شهادة الإقرار قد تم ضبط علامة TRUSTED_CONFIRMATION_REQUIRED فيها، ما يشير إلى أنّ مفتاح التوقيع يتطلب تأكيد مستخدم موثوق. إذا كان مفتاح التوقيع هو مفتاح RSA، تحقَّق من أنّه لا يحتوي على PURPOSE_ENCRYPT أو PURPOSE_DECRYPT السمة.
    3. ضَع علامة في المربّع extraData للتأكّد من أنّ رسالة التأكيد هذه تخصّ طلبًا جديدًا لم تتم معالجته بعد. تحمي هذه الخطوة من هجمات تسجيل الجلسات.
    4. يمكنك تحليل promptText للحصول على معلومات عن الإجراء أو الطلب الذي تم تأكيده. تذكَّر أنّ promptText هو الجزء الوحيد من الرسالة الذي confirmed المستخدم فعليًا. يجب ألا يفترض الطرف المُعتمِد أبدًا أنّ البيانات التي يجب تأكيدها والمضمّنة في extraData تتطابق مع promptText.
  5. أضِف منطقًا مشابهًا لما هو موضّح في المقتطف التالي من التعليمات البرمجية لعرض المربّع الحواري نفسه:

    Kotlin

    // This data structure varies by app type. This is an example.
      data class ConfirmationPromptData(val sender: String,
              val receiver: String, val amount: String)
    
      val myExtraData: ByteArray = byteArrayOf()
      val myDialogData = ConfirmationPromptData("Ashlyn", "Jordan", "$500")
      val threadReceivingCallback = Executor { runnable -> runnable.run() }
      val callback = MyConfirmationCallback()
    
      val dialog = ConfirmationPrompt.Builder(context)
              .setPromptText("${myDialogData.sender}, send
                              ${myDialogData.amount} to
                              ${myDialogData.receiver}?")
              .setExtraData(myExtraData)
              .build()
      dialog.presentPrompt(threadReceivingCallback, callback)
    

    Java

      // This data structure varies by app type. This is an example.
      class ConfirmationPromptData {
          String sender, receiver, amount;
          ConfirmationPromptData(String sender, String receiver, String amount) {
              this.sender = sender;
              this.receiver = receiver;
              this.amount = amount;
          }
      };
      final int MY_EXTRA_DATA_LENGTH = 100;
      byte[] myExtraData = new byte[MY_EXTRA_DATA_LENGTH];
      ConfirmationPromptData myDialogData = new ConfirmationPromptData("Ashlyn", "Jordan", "$500");
      Executor threadReceivingCallback = Runnable::run;
      MyConfirmationCallback callback = new MyConfirmationCallback();
      ConfirmationPrompt dialog = (new ConfirmationPrompt.Builder(getApplicationContext()))
              .setPromptText("${myDialogData.sender}, send ${myDialogData.amount} to ${myDialogData.receiver}?")
              .setExtraData(myExtraData)
              .build();
      dialog.presentPrompt(threadReceivingCallback, callback);
    

مصادر إضافية

لمزيد من المعلومات عن ميزة "التأكيد المحمي" في Android، يُرجى الرجوع إلى المراجع التالية:

المدوّنات