แนวทางปฏิบัติแนะนำสำหรับแบบฟอร์ม 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 กับเว็บไซต์จะทำให้หลอกให้ผู้ใช้ระบุรหัสแก่เว็บไซต์ที่เป็นอันตรายได้ยากขึ้น

การใช้รูปแบบนี้มีประโยชน์ 2 อย่างดังนี้

  • 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