วิธีทำ OTP ทาง SMS ให้ลูกค้าเข้าสู่ระบบได้อย่างปลอดภัย

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

เผยแพร่
เวลาอ่าน
4 นาที
ในโพสต์นี้ 6 หัวข้อ
  1. หน้าขอรหัสต้องตอบเหมือนกันทุกเบอร์
  2. จำกัดทั้งต่อเบอร์และต่อ IP
  3. กติกาของตัวรหัส
  4. รหัสรีเซ็ตรหัสผ่านต้องใช้ login ไม่ได้
  5. OTP login ต้องเจอด่านเดียวกับการ login ด้วยรหัสผ่าน
  6. กรณีที่อาจเจอในงานจริง

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

หน้าพวกนี้เปิดให้ใครก็ได้กรอกเบอร์ จึงเป็นหน้าที่คนนอกเข้าถึงง่ายที่สุดของระบบ และทุกครั้งที่ส่งรหัสคือค่า SMS ที่ร้านจ่าย โพสต์นี้รวมกติกาที่หน้าแบบนี้ควรมี วิธีส่ง SMS อยู่ใน ส่ง SMS จาก Laravel ผ่าน ThaiBulkSMS

หน้าขอรหัสต้องตอบเหมือนกันทุกเบอร์

สมมติหน้าขอรหัสตอบว่า "ไม่พบบัญชีนี้" เมื่อกรอกเบอร์ที่ไม่ได้สมัคร คนนอกก็ใช้หน้านี้ไล่เช็กได้ว่าเบอร์ไหนเป็นลูกค้าของร้าน หรือเป็นพนักงานในระบบ รายชื่อแบบนั้นมีค่าสำหรับคนที่จะโทรหลอกลูกค้า

หน้าขอรหัสจึงตอบข้อความเดียวกันทุกเบอร์ เช่น "ถ้ามีบัญชีนี้อยู่ เราได้ส่งรหัสไปให้แล้ว" แล้วส่ง SMS เฉพาะเบอร์ที่มีบัญชีจริง ร้านไม่เสียค่า SMS ให้เบอร์แปลกหน้า และคนที่กรอกก็แยกไม่ออกว่าเบอร์นั้นมีบัญชีหรือเปล่า

ข้อความที่เหมือนกันยังไม่พอ การจำกัดจำนวนครั้งต้องเหมือนกันด้วย ถ้าเบอร์ที่มีบัญชีโดนจำกัดหลังขอครบ 3 ครั้ง แต่เบอร์ที่ไม่มีบัญชีขอได้ไม่จำกัด คนนอกก็แค่ขอเบอร์ละสี่ครั้ง เบอร์ไหนขึ้นว่าขอบ่อยเกินไปก็คือเบอร์ที่มีบัญชี ระบบจึงนับโควต้าของทุกเบอร์ที่กรอกเข้ามา ไม่ว่าจะส่ง SMS จริงหรือไม่

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

จำกัดทั้งต่อเบอร์และต่อ IP

การจำกัดมีสองชั้น แต่ละชั้นกันคนละแบบ

ชั้น ตัวอย่างค่า กันอะไร
ต่อเบอร์ ตอนออกรหัส 3 รหัสต่อ 10 นาที คนที่ยิงเบอร์เดียวซ้ำ ๆ ให้เจ้าของเบอร์ได้ SMS รัว ๆ และร้านเสียเงิน
ต่อ IP ที่ route ขอรหัส 6 ครั้งต่อนาที เครื่องเดียวที่ไล่กรอกเบอร์ทีละเบอร์จากรายชื่อ
ต่อรหัส ตอนตรวจ ลองได้ 5 ครั้ง การสุ่มรหัส 6 หลัก

แยกการจำกัดต่อเบอร์ไว้ที่ตัวออกรหัส ทุกหน้าที่ออกรหัสจะได้กติกาเดียวกันโดยไม่ต้องจำ ส่วนการจำกัดต่อ IP อยู่ที่ route เพราะต้องรู้ request

การจำกัดต่อ IP มีความหมายก็ต่อเมื่อระบบเห็น IP จริงของผู้ใช้ ถ้าเว็บอยู่หลัง Cloudflare หรือ load balancer แล้วตั้งค่าไม่ถูก ทุก request จะดูเหมือนมาจาก IP เดียว ผู้ใช้ทั้งระบบจะโดนจำกัดพร้อมกัน หรือการจำกัดจะไม่ได้ผลเลย วิธีตั้งอยู่ใน ตั้ง trustProxies ให้ Laravel ที่อยู่หลัง Cloudflare

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

กติกาของตัวรหัส

  • 6 หลัก หมดอายุใน 10 นาที ลองได้ 5 ครั้ง รหัส 6 หลักมีล้านแบบ ปลอดภัยได้เพราะมีเพดานจำนวนครั้ง เกินแล้วแม้กรอกรหัสถูกก็ไม่ผ่าน ต้องขอรหัสใหม่
  • เก็บแบบ hash เหมือนรหัสผ่าน ถ้าตารางหลุด ข้อมูลที่หลุดไปก็เอาไปกรอกเป็นรหัสไม่ได้
  • ออกรหัสใหม่แล้วรหัสเก่าใช้ไม่ได้ มีรหัสที่ใช้ได้สองรหัสพร้อมกันเมื่อไหร่ รหัสที่ผู้ใช้ทิ้งไปแล้วก็ยังเปิดประตูได้
  • นับครั้งที่ลองให้ถูกแม้ request มาพร้อมกัน ถ้าอ่านจำนวนครั้งขึ้นมาก่อนแล้วค่อยบวกหนึ่ง คนที่ยิงหลาย request พร้อมกันจะลองได้เกิน 5 ครั้ง ต้องบวกและเช็กในคำสั่ง update เดียวกันในฐานข้อมูล
  • ลบรหัสที่ใช้แล้วหรือหมดอายุทิ้ง ตั้งงานรายชั่วโมงให้ลบ รหัสที่ใช้แล้วไม่ใช่ประวัติที่ต้องเก็บ

ถ้าไม่อยากเขียนส่วนนี้เอง ผู้ให้บริการ SMS บางเจ้าอย่าง ThaiBulkSMS มี API สำหรับ OTP โดยเฉพาะ ส่งเบอร์ไป ผู้ให้บริการสร้างรหัส ส่ง SMS และตรวจรหัสให้ ระบบเราเก็บแค่ token ที่ได้กลับมา ทางนี้เริ่มได้เร็ว แต่กติกาเรื่องตอบเหมือนกันทุกเบอร์ การจำกัดต่อเบอร์และต่อ IP และการแยกจุดประสงค์ของรหัส ยังเป็นงานของระบบเราเหมือนเดิม

รหัสรีเซ็ตรหัสผ่านต้องใช้ login ไม่ได้

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

พอรีเซ็ตรหัสผ่านสำเร็จ ระบบควรทำอีกสองอย่าง

  • ออกจากระบบทุกเครื่อง คนที่รีเซ็ตรหัสผ่านอาจกำลังไล่คนอื่นออกจากบัญชี session ที่ค้างอยู่ของคนนั้นต้องหมดไปด้วย
  • ยกเลิกรหัส login ที่ยังไม่ได้ใช้ของเบอร์นั้น ไม่อย่างนั้นรหัสที่ส่งไปก่อนหน้าก็ยังเปิดประตูได้

OTP login ต้องเจอด่านเดียวกับการ login ด้วยรหัสผ่าน

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

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

กรณีที่อาจเจอในงานจริง

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

ตอนถัดไปของซีรีส์ ติดต่อและยืนยันตัวตนลูกค้า

อ่านต่อ

ล่าสุด

ดูทั้งหมด →