一般來說,系統會傳送簡訊,要求使用者提供一次性密碼 (OTP) 來確認身分。簡訊 OTP 的用途包括:
- 雙重驗證。除了使用者名稱和密碼,簡訊 OTP 也能做為強烈信號,證明帳戶屬於收到簡訊 OTP 的使用者。
- 驗證電話號碼。部分服務會將電話號碼做為使用者的主要 ID。在這些服務中,使用者可以輸入電話號碼和透過簡訊收到的 OTP,證明自己的身分。有時會與 PIN 碼搭配使用,構成雙重驗證。
- 帳戶救援。使用者無法登入帳戶時,必須有方法可以復原帳戶。常見的帳戶救援方法包括傳送電子郵件到註冊的電子郵件地址,或是傳送簡訊動態密碼到電話號碼。
- 確認付款:在付款系統中,部分銀行或信用卡發卡機構會基於安全考量,要求付款人進行額外驗證。簡訊 OTP 通常用於此目的。
請繼續閱讀,瞭解如何針對這些用途建構簡訊 OTP 表單的最佳做法。
檢查清單
如要透過簡訊驗證碼提供最佳使用者體驗,請按照下列步驟操作:
- 使用
<input>
元素,並搭配:type="text"
inputmode="numeric"
autocomplete="one-time-code"
- 在 OTP 簡訊的最後一行使用
@BOUND_DOMAIN #OTP_CODE
。 - 使用 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"
將行動裝置鍵盤切換為只顯示數字。
部分網站會將 type="tel"
用於動態密碼輸入欄位,因為這樣做也能在焦點移至該欄位時,將行動裝置鍵盤切換為僅顯示數字 (包括 *
和 #
)。過去在 inputmode="numeric"
尚未廣泛支援時,會使用這項變通方法。由於 Firefox 開始支援 inputmode="numeric"
,因此無須使用語意不正確的 type="tel"
駭客。
autocomplete="one-time-code"
autocomplete
屬性可讓開發人員指定瀏覽器提供自動完成輔助功能的權限,並告知瀏覽器欄位中預期的資訊類型。
有了 autocomplete="one-time-code"
,每當使用者在開啟表單時收到簡訊,作業系統就會以啟發式方式剖析簡訊中的一次性密碼,鍵盤也會建議使用者輸入該密碼。這項功能僅適用於 iOS、iPadOS 和 macOS 上的 Safari 12 以上版本,但我們強烈建議使用,因為這是改善這些平台簡訊 OTP 體驗的絕佳方式。
autocomplete="one-time-code"
實際運作情況。
autocomplete="one-time-code"
可提升使用者體驗,但您還可以確保簡訊符合以來源為準的訊息格式,進一步改善體驗。
選用屬性
選用屬性包括:
pattern
指定輸入的 OTP 必須符合的格式。使用規則運算式指定比對模式。舉例來說,\d{6}
會將一次性密碼限制為六位數的字串。如要進一步瞭解pattern
,請參閱「使用 JavaScript 進行更複雜的即時驗證」。required
表示使用者必須填寫該欄位。
如需更多建議,請參閱登入表單最佳做法。
設定簡訊文字格式
為提升輸入動態密碼的使用者體驗,我們將按照簡訊傳送的來源繫結一次性驗證碼規格,調整動態密碼的輸入方式。
格式規則的核心如下:在簡訊結尾加上收件者網域 (前面加上 @
),以及動態密碼 (前面加上 #
)。
例如:
Your OTP is 123456
@web-otp.glitch.me #123456
採用標準格式的 OTP 訊息可讓系統更輕鬆可靠地擷取驗證碼。 將一次性密碼與網站建立關聯,可避免使用者受騙,將密碼提供給惡意網站。
精確格式規則
精確規則如下:
- 訊息開頭可加入易讀文字,其中包含至少一個數字的四到十個英數字元字串,最後一行則保留給網址和一次性密碼。
- 呼叫 API 的網站網址網域部分必須以
@
開頭。 - 網址必須包含
#
,後面接著 OTP。字元數不得超過 140 個。
使用這種格式有幾個優點:
- OTP 會與網域綁定。如果使用者位於簡訊中指定的網域以外的網域,系統就不會顯示 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。呼叫 navigator.credentials.get()
並使用 otp
型別 (OTPCredential
),其中 transport
包含 sms
,網站就會等待符合來源繫結一次性代碼的簡訊送達,並由使用者授予存取權。將一次性密碼傳遞至 JavaScript 後,網站即可在表單中使用該密碼,或直接將密碼 POST 至伺服器。
navigator.credentials.get({
otp: {transport:['sms']}
})
.then(otp => input.value = otp.code);
如要詳細瞭解如何使用 WebOTP API,請參閱「使用 WebOTP API 驗證網頁上的電話號碼」,或複製並貼上下列程式碼片段。請務必在 <form>
中設定 action
和 method
屬性。
// 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);
});
});
}