簡訊動態密碼表單最佳做法

瞭解如何改善簡訊 OTP 表單,提升使用者體驗。

要求使用者提供透過簡訊傳送的動態密碼 (OTP),是確認使用者電話號碼的常見做法。簡訊 OTP 有幾種用途:

  • 雙重驗證:除了使用者名稱和密碼之外,簡訊 OTP 也可以做為強烈信號,表示帳戶屬於收到簡訊 OTP 的使用者。
  • 驗證電話號碼。部分服務會使用電話號碼做為使用者的主要識別碼。在這種服務中,使用者可以輸入自己的電話號碼和透過簡訊收到的 OTP,以證明自己的身分。有時會與 PIN 碼搭配使用,構成雙重驗證。
  • 帳戶救援。如果使用者無法登入帳戶,您必須提供復原方法。常見的帳戶復原方法是將電子郵件傳送至使用者的註冊電子郵件地址,或是將簡訊動態密碼傳送至使用者的電話號碼。
  • 付款確認 在付款系統中,部分銀行或信用卡發卡機構會基於安全考量,要求付款人進行額外驗證。通常會用簡訊 OTP 來做這件事。

本文將說明如何為上述用途建立簡訊 OTP 表單的最佳做法。

檢查清單

如要提供最佳的簡訊 OTP 使用者體驗,請按照下列步驟操作:

  • 使用 <input> 元素時,請搭配以下元素:
    • type="text"
    • inputmode="numeric"
    • autocomplete="one-time-code"
  • @BOUND_DOMAIN #OTP_CODE 做為 OTP 簡訊的最後一行。
  • 使用 WebOTP API

使用 <input> 元素

使用含有 <input> 元素的表單是最重要的最佳做法,因為它適用於所有瀏覽器。即使某些瀏覽器無法使用本篇文章提供的其他建議,使用者仍可手動輸入並提交 OTP。

<form action="/verify-otp" method="POST">
  <input type="text"
         inputmode="numeric"
         autocomplete="one-time-code"
         pattern="\d{6}"
         required>
</form>

以下提供幾個做法,確保輸入欄位能充分發揮瀏覽器功能。

type="text"

由於 OTP 通常是五或六位數字,因此在輸入欄位中使用 type="number" 可能會讓使用者覺得直覺,因為這會將行動鍵盤變更為僅限數字。我們不建議這麼做,因為瀏覽器會預期輸入欄位為可計數的數字,而非多個數字的序列,這可能會導致非預期的行為。使用 type="number" 會在輸入欄位旁邊顯示向上和向下按鈕;按下這些按鈕可增加或減少數字,並移除前面的零。

請改用 type="text"。這不會讓行動裝置鍵盤只顯示數字,但沒關係,因為下一個使用 inputmode="numeric" 的提示會完成這項工作。

inputmode="numeric"

使用 inputmode="numeric" 將手機鍵盤變更為僅輸入數字。

有些網站會在 OTP 輸入欄位使用 type="tel",因為在聚焦時,這項元素也會將行動鍵盤切換為僅顯示數字 (包括 *#)。在過去 inputmode="numeric" 未廣泛支援時,就曾使用過這項駭客手法。由於 Firefox 開始支援 inputmode="numeric",因此您不必使用語義不正確的 type="tel" 駭客攻擊。

autocomplete="one-time-code"

autocomplete 屬性可讓開發人員指定瀏覽器提供自動完成協助所需的權限,並告知瀏覽器該欄位預期的資訊類型。

有了 autocomplete="one-time-code",只要使用者在開啟表單時收到簡訊,作業系統就會以啟發式剖析簡訊中的 OTP,並在鍵盤上顯示 OTP,供使用者輸入。這項功能僅適用於 iOS、iPadOS 和 macOS 上的 Safari 12 以上版本,但我們強烈建議您使用這項功能,因為這是改善這些平台上簡訊 OTP 體驗的簡單方法。

`autocomplete="one-time-code"` 實際運作。

autocomplete="one-time-code" 可改善使用者體驗,但您還可以確保簡訊符合原始目的地訊息格式,進一步提升使用者體驗。

設定簡訊文字格式

遵循透過簡訊傳送的來源限定一次性驗證碼規格,改善使用者輸入 OTP 的體驗。

格式規則很簡單:在簡訊中加入收件者網域,前面加上 @,並在動態密碼前面加上 #

例如:

Your OTP is 123456

@web-otp.glitch.me #123456

使用標準格式傳送動態密碼訊息,可讓您更輕鬆且可靠地從訊息中擷取驗證碼。將 OTP 驗證碼與網站建立關聯,可讓使用者更難受騙,將驗證碼提供給惡意網站。

使用這個格式有幾個優點:

  • OTP 會繫結至網域。如果使用者位於 SMS 訊息中指定的網域以外的網域,系統就不會顯示一次性密碼建議。這也能降低網路釣魚攻擊和潛在帳戶盜用風險。
  • 瀏覽器現在可以可靠地擷取 OTP,而不需要依賴神秘且不穩定的啟發式。

當網站使用 autocomplete="one-time-code" 時,搭載 iOS 14 以上版本的 Safari 會依照上述規則建議 OTP。

這類簡訊格式也適用於 Safari 以外的瀏覽器。Android 版 Chrome、Opera 和 Vivaldi 也支援使用 WebOTP API 的來源綁定一次性密碼規則,但不支援透過 autocomplete="one-time-code" 執行。

使用 WebOTP API

WebOTP API 可讓您存取在簡訊中收到的 OTP。透過 transport 包含 smsotp 類型 (OTPCredential) 呼叫 navigator.credentials.get(),網站會等待符合來源綁定一次性代碼的簡訊傳送,並由使用者授予存取權。傳送 OTP 至 JavaScript 後,網站就能在表單中使用這組驗證碼,或直接將其 POST 至伺服器。

navigator.credentials.get({
  otp: {transport:['sms']}
})
.then(otp => input.value = otp.code);
WebOTP API 運作情形。

如要進一步瞭解如何使用 WebOTP API,請參閱「使用 WebOTP API 驗證網站上的電話號碼」一文,或複製並貼上以下程式碼片段。(請確認 <form> 元素已正確設定 actionmethod 屬性)。

// Feature detection
if ('OTPCredential' in window) {
  window.addEventListener('DOMContentLoaded', e => {
    const input = document.querySelector('input[autocomplete="one-time-code"]');
    if (!input) return;
    // Cancel the WebOTP API if the form is submitted manually.
    const ac = new AbortController();
    const form = input.closest('form');
    if (form) {
      form.addEventListener('submit', e => {
        // Cancel the WebOTP API.
        ac.abort();
      });
    }
    // Invoke the WebOTP API
    navigator.credentials.get({
      otp: { transport:['sms'] },
      signal: ac.signal
    }).then(otp => {
      input.value = otp.code;
      // Automatically submit the form when an OTP is obtained.
      if (form) form.submit();
    }).catch(err => {
      console.log(err);
    });
  });
}

相片來源:Jason LeungUnsplash 網站上提供。