แนวทางปฏิบัติแนะนำสำหรับแบบฟอร์ม SMS OTP

ดูวิธีเพิ่มประสิทธิภาพแบบฟอร์ม OTP ทาง SMS และปรับปรุงประสบการณ์ของผู้ใช้

การขอให้ผู้ใช้ระบุ OTP (รหัสผ่านที่สามารถใช้งานได้เพียงครั้งเดียว) ที่ส่งผ่าน SMS เป็นวิธีทั่วไปในการยืนยันหมายเลขโทรศัพท์ของผู้ใช้ กรณีการใช้งาน OTP ทาง SMS มีดังนี้

  • การตรวจสอบสิทธิ์แบบ 2 ปัจจัย นอกจากชื่อผู้ใช้และรหัสผ่านแล้ว OTP ทาง SMS ยังใช้เป็นสัญญาณที่ชัดเจนว่าบัญชีเป็นของบุคคลที่ได้รับ OTP ทาง SMS
  • การยืนยันหมายเลขโทรศัพท์ บริการบางอย่างใช้หมายเลขโทรศัพท์เป็นตัวระบุหลักของผู้ใช้ ในบริการดังกล่าว ผู้ใช้จะป้อนหมายเลขโทรศัพท์และ OTP ที่ได้รับทาง SMS เพื่อพิสูจน์ตัวตนได้ บางครั้งก็ใช้ร่วมกับ PIN เพื่อใช้ตรวจสอบสิทธิ์แบบ 2 ปัจจัย
  • การกู้คืนบัญชี เมื่อผู้ใช้สูญเสียสิทธิ์เข้าถึงบัญชี จะต้องมีวิธีกู้คืนบัญชี การส่งอีเมลไปยังอีเมลที่ลงทะเบียนไว้หรือส่ง OTP ทาง SMS ไปยังหมายเลขโทรศัพท์เป็นวิธีการกู้คืนบัญชีที่พบได้ทั่วไป
  • การยืนยันการชำระเงิน ในระบบการชำระเงิน ธนาคารหรือผู้ออกบัตรเครดิตบางรายจะขอการตรวจสอบสิทธิ์เพิ่มเติมจากผู้ชำระเงินเพื่อเหตุผลด้านความปลอดภัย โดยปกติแล้วระบบจะใช้ OTP ทาง SMS เพื่อวัตถุประสงค์ดังกล่าว

โพสต์นี้จะอธิบายแนวทางปฏิบัติแนะนำในการสร้างแบบฟอร์ม OTP ทาง SMS สำหรับกรณีการใช้งานข้างต้น

เช็กลิสต์

ทำตามขั้นตอนต่อไปนี้เพื่อให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ดีที่สุดด้วย OTP ทาง SMS

  • ใช้องค์ประกอบ <input> กับสิ่งต่อไปนี้
    • type="text"
    • inputmode="numeric"
    • autocomplete="one-time-code"
  • ใช้ @BOUND_DOMAIN #OTP_CODE เป็นบรรทัดสุดท้ายของข้อความ OTP ทาง SMS
  • ใช้ 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 มักจะเป็นตัวเลข 5 หรือ 6 หลัก การใช้ type="number" สำหรับช่องป้อนข้อมูลจึงดูเป็นวิธีที่เข้าใจง่ายเนื่องจากจะเปลี่ยนแป้นพิมพ์ของอุปกรณ์เคลื่อนที่เป็นตัวเลขเท่านั้น ไม่แนะนําให้ทําเช่นนี้ เนื่องจากเบราว์เซอร์คาดหวังว่าช่องป้อนข้อมูลจะเป็นจํานวนนับได้ ไม่ใช่ลําดับของจํานวนหลายรายการ ซึ่งอาจทําให้ระบบทํางานอย่างไม่คาดคิด การใช้ type="number" จะทําให้ปุ่มขึ้นและลงแสดงข้างช่องป้อนข้อมูล การกดปุ่มเหล่านี้จะเพิ่มหรือลดตัวเลขและอาจนําเลข 0 นําหน้าออก

โปรดใช้ type="text" แทน ซึ่งจะไม่เปลี่ยนแป้นพิมพ์บนอุปกรณ์เคลื่อนที่ให้แสดงเฉพาะตัวเลข แต่ไม่เป็นไรเพราะเคล็ดลับถัดไปในการใช้ inputmode="numeric" จะทำสิ่งนั้น

inputmode="numeric"

ใช้ inputmode="numeric" เพื่อเปลี่ยนแป้นพิมพ์บนอุปกรณ์เคลื่อนที่เป็นตัวเลขเท่านั้น

บางเว็บไซต์ใช้ type="tel" สำหรับช่องป้อน OTP เนื่องจากจะเปลี่ยนแป้นพิมพ์บนอุปกรณ์เคลื่อนที่เป็นตัวเลขเท่านั้น (รวมถึง * และ #) เมื่อโฟกัส การแฮ็กนี้เคยใช้ในอดีตตอนที่ยังไม่มีการสนับสนุน inputmode="numeric" อย่างกว้างขวาง เนื่องจาก Firefox เริ่มรองรับ inputmode="numeric" แล้ว คุณจึงไม่จำเป็นต้องใช้แฮ็ก type="tel" ที่ไม่ถูกต้องในเชิงความหมาย

autocomplete="one-time-code"

แอตทริบิวต์ autocomplete ช่วยให้คุณระบุสิทธิ์ที่เบราว์เซอร์ต้องมีเพื่อให้ความช่วยเหลือในการเติมข้อความอัตโนมัติ และแจ้งให้เบราว์เซอร์ทราบเกี่ยวกับประเภทข้อมูลที่คาดหวังในช่อง

เมื่อใช้ autocomplete="one-time-code" เมื่อใดก็ตามที่ผู้ใช้ได้รับข้อความ SMS ขณะที่แบบฟอร์มเปิดอยู่ ระบบปฏิบัติการจะแยกวิเคราะห์ OTP ใน SMS โดยใช้วิธีการเฮิวริสติก และแป้นพิมพ์จะแนะนำ OTP ให้ผู้ใช้ป้อน ซึ่งใช้ได้เฉพาะใน Safari 12 ขึ้นไปใน iOS, iPadOS และ macOS แต่เราขอแนะนําอย่างยิ่งให้ใช้ฟีเจอร์นี้ เนื่องจากเป็นวิธีที่ง่ายในการปรับปรุงประสบการณ์การใช้งาน OTP ทาง SMS ในแพลตฟอร์มเหล่านั้น

`autocomplete="one-time-code"` ทำงานอยู่

autocomplete="one-time-code" ช่วยปรับปรุงประสบการณ์ของผู้ใช้ แต่คุณยังทำได้อีกมากมายโดยตรวจสอบว่าข้อความ SMS เป็นไปตามรูปแบบข้อความที่เชื่อมโยงกับต้นทาง

จัดรูปแบบข้อความ SMS

ปรับปรุงประสบการณ์ของผู้ใช้ในการป้อน OTP โดยปรับให้สอดคล้องกับข้อกำหนดของรหัสแบบครั้งเดียวที่ผูกกับต้นทางซึ่งส่งผ่าน SMS

กฎการจัดรูปแบบนั้นง่ายมาก เพียงใส่โดเมนของผู้รับต่อท้ายข้อความ SMS โดยนำหน้าด้วย @ และใส่ OTP โดยนำหน้าด้วย #

เช่น

Your OTP is 123456

@web-otp.glitch.me #123456

การใช้รูปแบบมาตรฐานสำหรับข้อความ OTP จะทำให้การดึงรหัสจากข้อความดังกล่าวง่ายและน่าเชื่อถือยิ่งขึ้น การเชื่อมโยงรหัส OTP กับเว็บไซต์จะทำให้หลอกลวงให้ผู้ใช้ระบุรหัสแก่เว็บไซต์ที่เป็นอันตรายได้ยากขึ้น

การใช้รูปแบบนี้มีข้อดีหลายประการ ได้แก่

  • OTP จะเชื่อมโยงกับโดเมน หากผู้ใช้อยู่ในโดเมนอื่นนอกเหนือจากโดเมนที่ระบุไว้ในข้อความ SMS คําแนะนํา OTP จะไม่ปรากฏ ซึ่งจะช่วยลดความเสี่ยงจากการโจมตีแบบฟิชชิงและการลักลอบใช้บัญชีที่อาจเกิดขึ้นด้วย
  • ตอนนี้เบราว์เซอร์จะสามารถดึง OTP ได้อย่างน่าเชื่อถือโดยไม่ต้องอาศัยการเรียนรู้ที่ลึกลับและไม่สม่ำเสมอ

เมื่อเว็บไซต์ใช้ autocomplete="one-time-code" ทาง Safari ที่ใช้ iOS 14 ขึ้นไปจะแนะนำ OTP ตามกฎข้างต้น

รูปแบบข้อความ SMS นี้ยังเป็นประโยชน์ต่อเบราว์เซอร์อื่นๆ นอกเหนือจาก Safari ด้วย Chrome, Opera และ Vivaldi ใน Android ยังรองรับกฎเกี่ยวกับรหัสแบบครั้งเดียวที่เชื่อมโยงกับต้นทางด้วย WebOTP API ด้วย แต่ไม่รองรับผ่าน autocomplete="one-time-code"

ใช้ WebOTP API

WebOTP API ให้สิทธิ์เข้าถึง OTP ที่ได้รับในข้อความ SMS เมื่อเรียกใช้ navigator.credentials.get() ด้วยประเภท otp (OTPCredential) โดยที่ transport มี sms อยู่ เว็บไซต์จะรอให้มีการส่ง SMS ที่สอดคล้องกับรหัสแบบใช้ครั้งเดียวซึ่งผูกกับต้นทาง ส่งและให้สิทธิ์เข้าถึงแก่ผู้ใช้ เมื่อส่ง OTP ไปยัง JavaScript แล้ว เว็บไซต์จะใช้ OTP ในแบบฟอร์มหรือ POST ไปยังเซิร์ฟเวอร์โดยตรงได้

navigator.credentials.get({
  otp: {transport:['sms']}
})
.then(otp => input.value = otp.code);
การทำงานของ WebOTP API

ดูวิธีใช้ 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);
    });
  });
}

รูปภาพโดย Jason Leung จาก Unsplash