安全的使用者驗證

為保護 Android 中的驗證系統,請考慮改用其他密碼模型,尤其是對於使用者的銀行和電子郵件帳戶這類敏感帳戶。提醒您,使用者安裝的某些應用程式可能沒有最好的意圖,也可能試圖騙取使用者。

此外,請勿假設只有授權使用者會使用裝置。手機盜竊是常見的問題,攻擊者會鎖定未鎖定的裝置,直接透過使用者資料或金融應用程式營利。我們建議所有敏感的應用程式都採用合理的驗證逾時時間 (15 分鐘?),並需要進行生物特徵辨識驗證,且需要先完成額外的驗證程序,才能進行匯款等敏感動作。

生物特徵辨識驗證對話方塊

生物特徵辨識程式庫提供一組函式,用於顯示要求生物特徵辨識驗證的提示,例如臉部辨識或指紋辨識。不過,生物特徵辨識提示可設為改回使用 LSKF,後者俱有已知的應接字元瀏覽風險。針對敏感的應用程式,我們建議不要讓生物特徵辨識功能改回使用 PIN 碼,而且使用者用完生物特徵辨識重試後,即可等待,或以密碼重新登入或重設帳戶。重設帳戶時,應要求裝置無法輕易存取的因素 (最佳做法如下)。

這項做法如何協助防範詐欺和手機遭竊

預防詐欺的特定使用案例是,在交易前要求在應用程式中要求生物特徵辨識驗證。當使用者想進行金融交易時,系統會顯示生物特徵辨識對話方塊,用於驗證交易的確實是進行交易的指定使用者。無論攻擊者是否知道 LSKF,這項最佳做法都能防範攻擊者竊取裝置,因為他們需要探測他們是裝置的擁有者。

為了提高安全性,建議應用程式開發人員要求使用第 3 級生物特徵辨識驗證,並使用 CryptoObject 進行銀行和金融交易。

導入作業

  1. 請務必納入 androidx.biometric 程式庫。
  2. 在活動或片段中加入生物特徵辨識登入對話方塊,其中保留您要驗證使用者的邏輯。

Kotlin


private var executor: Executor? = null
private var biometricPrompt: BiometricPrompt? = null
private var promptInfo: BiometricPrompt.PromptInfo? = null

fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_login)
  executor = ContextCompat.getMainExecutor(this)
  biometricPrompt = BiometricPrompt(this@MainActivity,
    executor, object : AuthenticationCallback() {
      fun onAuthenticationError(
        errorCode: Int,
        @NonNull errString: CharSequence
      ) {
        super.onAuthenticationError(errorCode, errString)
        Toast.makeText(
          getApplicationContext(),
          "Authentication error: $errString", Toast.LENGTH_SHORT
        )
          .show()
      }

      fun onAuthenticationSucceeded(
        @NonNull result: BiometricPrompt.AuthenticationResult?
      ) {
        super.onAuthenticationSucceeded(result)
        Toast.makeText(
          getApplicationContext(),
          "Authentication succeeded!", Toast.LENGTH_SHORT
        ).show()
      }

      fun onAuthenticationFailed() {
        super.onAuthenticationFailed()
        Toast.makeText(
          getApplicationContext(), "Authentication failed",
          Toast.LENGTH_SHORT
        )
          .show()
      }
    })
  promptInfo = Builder()
    .setTitle("Biometric login for my app")
    .setSubtitle("Log in using your biometric credential")
    .setNegativeButtonText("Use account password")
    .build()

  // Prompt appears when user clicks "Log in".
  // Consider integrating with the keystore to unlock cryptographic operations,
  // if needed by your app.
  val biometricLoginButton: Button = findViewById(R.id.biometric_login)
  biometricLoginButton.setOnClickListener { view ->
    biometricPrompt.authenticate(
      promptInfo
    )
  }
}

Java


private Executor executor;
private BiometricPrompt biometricPrompt;
private BiometricPrompt.PromptInfo promptInfo;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
    executor = ContextCompat.getMainExecutor(this);
    biometricPrompt = new BiometricPrompt(MainActivity.this,
            executor, new BiometricPrompt.AuthenticationCallback() {
        @Override
        public void onAuthenticationError(int errorCode,
                @NonNull CharSequence errString) {
            super.onAuthenticationError(errorCode, errString);
            Toast.makeText(getApplicationContext(),
                "Authentication error: " + errString, Toast.LENGTH_SHORT)
                .show();
        }

        @Override
        public void onAuthenticationSucceeded(
                @NonNull BiometricPrompt.AuthenticationResult result) {
            super.onAuthenticationSucceeded(result);
            Toast.makeText(getApplicationContext(),
                "Authentication succeeded!", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onAuthenticationFailed() {
            super.onAuthenticationFailed();
            Toast.makeText(getApplicationContext(), "Authentication failed",
                Toast.LENGTH_SHORT)
                .show();
        }
    });

    promptInfo = new BiometricPrompt.PromptInfo.Builder()
            .setTitle("Biometric login for my app")
            .setSubtitle("Log in using your biometric credential")
            .setNegativeButtonText("Use account password")
            .build();

    // Prompt appears when the user clicks "Log in".
    // Consider integrating with the keystore to unlock cryptographic operations,
    // if needed by your app.
    Button biometricLoginButton = findViewById(R.id.biometric_login);
    biometricLoginButton.setOnClickListener(view -> {
            biometricPrompt.authenticate(promptInfo);
    });
}

最佳做法

建議您先從程式碼研究室開始,進一步瞭解生物特徵辨識。

視用途而定,無論使用者是否有明確操作,您都可以實作對話方塊。為防範詐欺行為,建議您新增生物特徵辨識對話方塊,並在每筆交易中明確提供使用者明確的操作動作。我們瞭解新增驗證機制會對使用者體驗帶來阻礙,但由於銀行交易中要處理的資訊性質,且生物特徵辨識驗證作業比其他驗證方法更順暢,我們認為必須新增這種瀏覽機制。

進一步瞭解生物特徵辨識驗證

密碼金鑰

密碼金鑰是比密碼更安全、更簡單的替代驗證方法。密碼金鑰採用公開金鑰密碼編譯技術,讓使用者能夠使用裝置的螢幕鎖定機制 (例如指紋或臉部辨識) 登入應用程式和網站。這樣使用者就不必費心記住及管理密碼,安全性大幅提升。

只要一個步驟,密碼金鑰就能滿足多重驗證的要求。這個機制會取代密碼和動態密碼,因此能有效防範網路釣魚攻擊,避免使用簡訊或應用程式的動態密碼,導致使用者體驗大打折扣。由於密碼金鑰已標準化,因此單一實作可讓所有使用者的裝置、瀏覽器和作業系統享有無密碼的使用體驗。

在 Android 上,系統會使用 Credential Manager Jetpack 程式庫支援密碼金鑰,該程式庫整合主要驗證方法,包括密碼金鑰、密碼和聯合登入 (例如「使用 Google 帳戶登入」)。

這項機制如何協助防範詐欺行為

密碼金鑰只能用於您註冊的應用程式和網站,因此可防範網路釣魚攻擊。

密碼金鑰的核心元件是加密編譯金鑰。一般來說,這個私密金鑰只會儲存在您的裝置上 (例如筆電或手機),並會透過 Google 密碼管理工具等憑證提供者 (又稱為密碼管理工具) 同步處理到這些裝置上。建立密碼金鑰時,線上服務只會儲存對應的公開金鑰。服務會在登入時使用私密金鑰,透過公開金鑰簽署驗證要求。這可能來自您的其中一部裝置。此外,您必須解鎖裝置或憑證存放區,防止未經授權的登入行為 (例如透過失竊的手機登入)。

為避免在裝置遭竊且已解鎖的情況下發生未經授權的存取行為,密碼金鑰必須與合理的驗證逾時期限結合。因為上一個使用者已經登入,竊取裝置的攻擊者不得使用應用程式。憑證應以固定間隔 (例如每 15 分鐘) 到期,使用者也應要求透過螢幕鎖定重新驗證來驗證身分。

如果手機遭竊,密碼金鑰可保護資料安全,因為竊賊無法竊取密碼,進而使用其他裝置使用 (密碼金鑰會視裝置而異)。如果您使用 Google 密碼管理工具,但手機遭竊,可以使用其他裝置 (例如電腦) 登入 Google 帳戶,然後從遠端從遭竊的手機中登出。這會導致失竊手機上的 Google 密碼管理工具無法使用,包括任何已儲存的密碼金鑰。

在最壞的情況下,如果失竊的裝置未復原,建立並同步密碼金鑰的憑證提供者會把密碼金鑰同步處理回新裝置。舉例來說,使用者可能會選擇使用 Google 密碼管理工具建立密碼金鑰,這時只要重新登入 Google 帳戶,並提供舊裝置的螢幕鎖定功能,就能在新裝置上存取密碼金鑰。

詳情請參閱「Google 密碼管理工具中的密碼金鑰安全性」一文。

導入作業

搭載 Android 9 (API 級別 28) 以上版本的裝置支援密碼金鑰。Android 4.4 以上版本支援密碼和「使用 Google 帳戶登入」功能。如要開始使用密碼金鑰,請按照下列步驟操作:

  1. 參考 Credential Manager 程式碼研究室,初步瞭解如何實作密碼金鑰。
  2. 詳閱密碼金鑰使用者體驗設計指南。本文件說明適合您的用途的建議流程。
  3. 按照指南學習 Credential Manager。
  4. 規劃應用程式的 Credential Manager 和密碼金鑰實作方式,規劃新增 Digital Asset Links 支援功能。

如要進一步瞭解如何建立、註冊密碼金鑰,以及使用密碼金鑰進行驗證,請參閱開發人員說明文件。

安全重設帳戶

未經授權人士可以存取已解鎖裝置 (例如偷竊手機時),會嘗試存取敏感應用程式,尤其是銀行或現金應用程式。如果應用程式實作生物特徵辨識驗證,攻擊者會嘗試重設帳戶才能進入。重設流程時,不可僅仰賴裝置上可輕鬆存取的資訊,例如電子郵件或簡訊動態密碼重設連結。

以下是可納入應用程式重設流程的常見最佳做法:

  • 臉部辨識和動態密碼
  • 安全提示問題
  • 知識因素 (例如母親的婚前姓氏、城市或最喜歡的歌曲)
  • 身分驗證程序

SMS Retriever API

SMS Retriever API 可讓您在 Android 應用程式中自動執行簡訊的使用者驗證。如此一來,使用者就不需要手動輸入驗證碼。此外,這個 API 不會要求使用者授予其他可能危險的應用程式權限,例如 RECEIVE_SMSREAD_SMS。不過,請勿將簡訊當做唯一使用者驗證機制,以避免在未經授權的情況下存取裝置。

這項機制如何協助防範詐欺行為

部分使用者會使用簡訊代碼做為唯一的驗證方式,為詐欺的進入點提供簡便的進入點。

SMS Retriever API 可讓應用程式不需使用者互動即直接擷取簡訊代碼,並且提供防範詐欺的保護措施。

導入作業

實作 SMS Retriever API 分為兩個部分:Android 和伺服器。

Android:(指南)

  1. 取得使用者的電話號碼。
  2. 啟動簡訊擷取程式用戶端。
  3. 將電話號碼傳送至你的伺服器。
  4. 接收驗證郵件。
  5. 將動態密碼傳送至您的伺服器。

伺服器:(指南)

  1. 建構驗證訊息。
  2. 透過簡訊傳送驗證訊息。
  3. 系統傳回動態密碼時進行驗證。

最佳做法

應用程式整合完成,且使用者電話號碼通過 SMS Retriever API 驗證後,就會嘗試取得動態密碼。如果傳送成功,表示簡訊會自動收到在裝置上。如果要求未成功,且使用者必須手動輸入動態密碼,則這可能是警告徵兆,表示使用者可能遇到詐欺。

請勿利用簡訊做為唯一的使用者驗證機制,因為這類機制可以留下本機攻擊空間,例如入侵已解鎖裝置的攻擊者;或是 SIM 卡複製攻擊。建議盡可能使用生物特徵辨識功能。在無法使用生物特徵辨識感應器的裝置上,使用者驗證作業應仰賴至少一個難以從目前裝置取得的因素。

瞭解詳情

如要進一步瞭解最佳做法,請參閱下列資源: