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

瞭解如何最佳化簡訊動態密碼表單,並改善使用者體驗。

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

  • 雙重驗證。除了使用者名稱和密碼外,簡訊動態密碼還能做為高強度信號,表明帳戶為接收簡訊動態密碼的使用者所有。
  • 驗證電話號碼。部分服務會使用電話號碼做為使用者的主要 ID。在這類服務中,使用者可以輸入電話號碼和透過簡訊收到的動態密碼,藉此驗證身分。有時會與 PIN 碼結合,以構成雙重驗證。
  • 帳戶救援。當使用者失去帳戶存取權時,您需要能夠找方法復原帳戶。傳送電子郵件至註冊的電子郵件地址,或發送簡訊動態密碼給對方的電話號碼,是常見的帳戶救援方式。
  • 付款確認:基於安全考量,在付款系統中,部分銀行或信用卡發卡機構會要求付款人進行額外驗證。簡訊動態密碼通常用於這類用途。

本文針對上述用途說明建構簡訊動態密碼表單的最佳做法。

檢查清單

如要讓簡訊動態密碼提供最佳使用者體驗,請按照下列步驟操作:

  • 請搭配使用 <input> 元素和:
    • type="text"
    • inputmode="numeric"
    • autocomplete="one-time-code"
  • 請將 @BOUND_DOMAIN #OTP_CODE 設為動態密碼簡訊的最後一行。
  • 使用 WebOTP API

使用 <input> 元素

使用含有 <input> 元素的表單是最重要的最佳做法,因為這項功能適用於所有瀏覽器。即使這篇文章提供的其他建議無法在某些瀏覽器中運作,使用者仍可自行輸入並提交動態密碼。

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

以下幾個提案,可確保輸入欄位能充分運用瀏覽器功能。

type="text"

動態密碼通常為 5 或 6 位數字,因此在輸入欄位使用 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",每當使用者在表單開啟時收到簡訊,作業系統就會以啟發式方式剖析簡訊中的動態密碼,鍵盤也會建議使用者輸入的動態密碼。這種方法僅適用於 iOS、iPadOS 和 macOS 上的 Safari 12 以上版本,但我們強烈建議您採用,因為這樣可以輕鬆改善這些平台上的簡訊動態密碼體驗。

「autocomplete="one-time-code"」實際運作情況。

autocomplete="one-time-code" 可改善使用者體驗,但您可以確保簡訊符合來源傳送的訊息格式,也可以採取其他做法。

設定簡訊文字格式

啟用來源繫結的一次性代碼 (透過簡訊傳送的一次性代碼),改善進入動態密碼的使用者體驗。

這個格式規則很簡單:使用接收者網域在簡訊前面加上 @,然後在簡訊開頭加上 # 的動態密碼。

例如:

Your OTP is 123456

@web-otp.glitch.me #123456

使用動態密碼訊息的標準格式可簡化從這類訊息擷取程式碼,不僅更加可靠。將動態密碼 (OTP) 代碼與網站建立關聯後,會比較難誘騙使用者向惡意網站提供程式碼。

使用這種格式有幾項好處:

  • 系統會將動態密碼繫結至網域。如果使用者所在的網域不是簡訊中指定的網域,系統就不會顯示動態密碼建議。這種做法也可降低網路釣魚攻擊和潛在帳戶盜用的風險。
  • 瀏覽器現在可以穩定地擷取 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 類型 (OTPCredential) 呼叫 navigator.credentials.get(),其中 transport 包含 sms,網站會等待符合來源限定的一次性代碼的簡訊,並由使用者授予存取權。將動態密碼傳送至 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 上。