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

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

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

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

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

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

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

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

  3. أرسِل تفاصيل المعاملة إلى خادمك واطلب منه إنشاء كائن ثنائي كبير (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 هو بنية بيانات CBOR تحتوي، من بين التفاصيل الأخرى، على نص الطلب الذي اطّلع عليه المستخدم بالإضافة إلى البيانات الإضافية التي مرّرتها إلى أداة إنشاء ConfirmationPrompt. استخدِم المفتاح الذي تم إنشاؤه سابقًا لتوقيع الكائن الثنائي الكبير dataThatWasConfirmed، ثم مرِّر هذا الكائن الثنائي الكبير، بالإضافة إلى التوقيع وتفاصيل المعاملة، مرة أخرى إلى الطرف المستفيد.

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

    1. تحقَّق من التوقيع على الرسالة بالإضافة إلى سلسلة شهادات الإثبات الخاصة بمفتاح التوقيع.
    2. تحقَّق من أنّ شهادة الإثبات تحتوي على العلامة TRUSTED_CONFIRMATION_REQUIRED، ما يشير إلى أنّ مفتاح التوقيع يتطلّب تأكيدًا موثوقًا من المستخدم. إذا كان مفتاح التوقيع هو مفتاح RSA، تحقَّق من أنّه لا يحتوي على السمة PURPOSE_ENCRYPT أو PURPOSE_DECRYPT.
    3. تحقَّق من extraData للتأكّد من أنّ رسالة التأكيد هذه تنتمي إلى طلب جديد ولم تتم معالجتها بعد. تحمي هذه الخطوة من هجمات إعادة التشغيل.
    4. حلِّل promptText للحصول على معلومات حول الإجراء أو الطلب الذي تم تأكيده. تذكَّر أنّ promptText هو الجزء الوحيد من الرسالة الذي أكّده المستخدم فعليًا. يجب ألا يفترض الطرف المستفيد أبدًا أنّ البيانات المطلوب تأكيدها والمضمّنة في 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"، يُرجى الاطّلاع على المراجع التالية.

المدوّنات