หน้าลืมรหัสผ่านดูเหมือนเป็นฟอร์มเล็ก ๆ แต่เชื่อมต่อกับงานที่มีต้นทุนจริง ทั้งการค้นหาบัญชี การสร้างโทเค็น และการส่งอีเมล หากปล่อยให้เรียกได้ไม่จำกัด บอตสามารถสร้างภาระให้ระบบ ส่งอีเมลรบกวนเจ้าของบัญชี หรือใช้ข้อความตอบกลับเพื่อเดาว่าอีเมลใดสมัครไว้กับเว็บไซต์
ตอนปรับระบบของ ParitLAB เราตั้งเป้าหมายสองข้อพร้อมกัน: ลดคำขออัตโนมัติ และรักษาประสบการณ์ของคนที่ลืมรหัสผ่านจริง เราจึงเลือกการป้องกันหลายชั้นที่ทำงานอยู่เบื้องหลัง แทนการให้ผู้ใช้ทุกคนแก้ภาพหรือกดช่อง CAPTCHA
ชั้นที่ 1: ช่อง honeypot สำหรับโปรแกรมกรอกฟอร์ม
ฟอร์มมีช่องชื่อเว็บไซต์ที่ถูกซ่อนจากหน้าจอและข้ามจากลำดับการกด Tab คนปกติจะไม่เห็นและไม่กรอก แต่บอตแบบง่ายที่เติมทุกช่องมักใส่ข้อมูลลงไป เมื่อเซิร์ฟเวอร์พบว่าช่องนี้มีค่า คำขอจะถูกปฏิเสธก่อนค้นหาบัญชีหรือส่งอีเมล
ข้อดีคือไม่มีแรงเสียดทานต่อผู้ใช้และไม่ต้องเรียกบริการภายนอก ข้อจำกัดคือบอตที่อ่านโครงสร้างหน้าเว็บได้ดีอาจรู้ว่าควรข้ามช่องนี้ ดังนั้น honeypot จึงเป็นเพียงตัวกรองชั้นแรก
ชั้นที่ 2: ตรวจเวลาที่ใช้กรอกฟอร์ม
เมื่อเซิร์ฟเวอร์แสดงหน้าแบบฟอร์ม ระบบบันทึกเวลาไว้ใน session หากฟอร์มถูกส่งกลับมาเร็วกว่า 3 วินาที เราถือว่าไม่น่าใช่การพิมพ์โดยคนและหยุดคำขอนั้น วิธีนี้จัดการสคริปต์ที่ยิง POST ทันทีได้ดี โดยไม่ต้องเก็บข้อมูลส่วนตัวเพิ่ม
เราไม่ใช้เวลาขั้นต่ำเป็นหลักฐานเพียงอย่างเดียว เพราะผู้จัดการรหัสผ่านหรือเบราว์เซอร์สามารถกรอกอีเมลได้เร็ว และผู้โจมตีสามารถหน่วงเวลาได้ การตัดสินใจจึงต้องอาศัยชั้นถัดไปด้วย
ชั้นที่ 3: rate limit ที่ยังอยู่หลังรีสตาร์ต
ทุกคำขอที่ผ่านการตรวจเบื้องต้นจะถูกนับในฐานข้อมูล โดยเก็บค่าแฮชของ IP และอีเมลแทนข้อความต้นฉบับ ระบบอนุญาตไม่เกิน 10 ครั้งต่อ IP ต่อชั่วโมง และไม่เกิน 3 ครั้งต่อบัญชีต่อชั่วโมง ข้อมูลนับที่เก่ากว่า 24 ชั่วโมงจะถูกล้างออกระหว่างการทำงาน
การเก็บตัวนับในฐานข้อมูลสำคัญกว่าการเก็บในตัวแปรของโปรเซส เพราะเว็บไซต์ PHP อาจมีหลาย worker และโปรเซสเริ่มใหม่ได้ตลอด การล็อกและ transaction ทำให้คำขอพร้อมกันไม่สามารถผ่านช่องว่างจากการอ่านตัวนับเวลาเดียวกันได้ง่าย ๆ
อย่าเปิดเผยว่าบัญชีมีอยู่จริงหรือไม่
ไม่ว่าระบบจะพบอีเมล ถูกจำกัด หรือถูก honeypot จับ หน้าเว็บจะแสดงผลลัพธ์ทั่วไปแบบเดียวกัน หลักการนี้ลด account enumeration ซึ่งเป็นการลองรายชื่ออีเมลเพื่อค้นหาผู้ใช้ของระบบ การส่งอีเมลจะเกิดขึ้นเฉพาะเมื่อคำขอผ่านทุกชั้นและพบบัญชีเท่านั้น
สิ่งที่เราทดสอบ
- คำขอที่ส่งเร็วเกินไปต้องไม่ผ่าน
- ช่อง honeypot ที่มีค่าต้องถูกบล็อก
- สามคำขอแรกของบัญชีทดสอบผ่านได้ และครั้งที่สี่ถูกจำกัด
- บัญชีอื่นจากเงื่อนไขที่อนุญาตยังใช้งานได้
- หน้าไทยและอังกฤษตอบสถานะ HTTP 200 และยังมี CSRF token
- จำนวนบัญชี ไลเซนส์ คำสั่งซื้อ และรายการสนับสนุนไม่เปลี่ยนหลังติดตั้งตารางใหม่
ข้อจำกัดและสิ่งที่ต้องติดตาม
rate limit อาจกระทบผู้ใช้หลายคนที่ออกอินเทอร์เน็ตผ่าน IP เดียวกัน และผู้โจมตีที่กระจายหลาย IP ยังสามารถหลบขีดจำกัดต่อ IP ได้ เราจึงตั้งค่าให้พอรองรับการใช้งานปกติ และควรติดตามอัตราการบล็อก ปริมาณอีเมล และข้อร้องเรียนจริง หากรูปแบบการโจมตีเปลี่ยน ค่อยเพิ่มมาตรการที่เหมาะสม เช่น proof-of-work หรือ CAPTCHA เฉพาะคำขอที่มีความเสี่ยง แทนการเพิ่มภาระให้ทุกคนตั้งแต่แรก