ออกแบบ Remember Me ให้ปลอดภัยด้วย Session และ Persistent Token
การทำ Remember Me ที่ปลอดภัยไม่ควรเก็บรหัสผ่านหรือใช้โทเคนที่ไม่มีวันหมดอายุ แต่ควรใช้แนวทาง session ร่วมกับ persistent token ที่เพิกถอนได้และหมุนโทเคนทุกครั้งที่ใช้งานใหม่ได้

ออกแบบ Remember Me ให้ปลอดภัยด้วย Session และ Persistent Token
การทำฟังก์ชัน Remember Me บนหน้าเข้าสู่ระบบ เป็นสิ่งที่ช่วยให้ผู้ใช้กลับมาใช้งานได้สะดวกขึ้นโดยไม่ต้องกรอกรหัสผ่านใหม่ทุกครั้ง แต่ความสะดวกนี้ต้องไม่แลกมากับความเสี่ยงด้านความปลอดภัย แนวทางที่เหมาะสมคือการใช้ session ร่วมกับ persistent token แทนการเก็บรหัสผ่านไว้ในเครื่องของผู้ใช้
แนวคิดของ Remember Me ที่ถูกต้อง
Remember Me ไม่ได้หมายถึงการทำให้ผู้ใช้ล็อกอินค้างแบบไม่มีวันหมดอายุ แต่เป็นการช่วยให้ระบบสามารถจดจำการกลับมาของผู้ใช้ในช่วงเวลาที่กำหนดได้อย่างปลอดภัย เป้าหมายสำคัญคือ
- ผู้ใช้ไม่ต้องกรอกรหัสผ่านใหม่ทุกครั้ง
- ระบบยังสามารถเพิกถอนการจดจำอุปกรณ์ได้
- จำกัดความเสียหายหากอุปกรณ์สูญหายหรือโทเคนรั่วไหล
ดังนั้น แนวคิดหลักคือทำให้ระบบ “จำอุปกรณ์” ได้ โดยไม่ต้องเก็บข้อมูลลับอย่างรหัสผ่านเอาไว้ในฝั่งผู้ใช้
สิ่งที่ไม่ควรทำอย่างเด็ดขาด
มีหลายแนวทางที่ดูง่าย แต่เสี่ยงอย่างมากและไม่ควรนำไปใช้จริง ได้แก่
- เก็บ password ไว้ใน LocalStorage หรือ Cookie
- ใช้ JWT อายุยาวมาก หรือไม่มีวันหมดอายุแทน Remember Me
- ส่งเพียง userId จากฝั่งไคลเอนต์ แล้วให้เซิร์ฟเวอร์เชื่อว่าเป็นผู้ใช้เดิม
วิธีเหล่านี้เพิ่มความเสี่ยงต่อการถูกขโมยข้อมูลหรือปลอมตัวเป็นผู้ใช้ได้ง่าย โดยเฉพาะเมื่อเกิด XSS, การรั่วไหลของข้อมูล หรือการเข้าถึงเครื่องของผู้ใช้โดยไม่ได้รับอนุญาต
แนวทางมาตรฐานที่ระบบขนาดใหญ่เลือกใช้
1) ตอนผู้ใช้ล็อกอิน
เมื่อผู้ใช้ล็อกอินสำเร็จ และเลือกติ๊ก Remember Me เซิร์ฟเวอร์ควรสร้าง Remember Token เพิ่มขึ้นมาอีกชุดหนึ่ง โทเคนนี้ต้องเป็นค่าสุ่มที่ยาวพอและคาดเดาไม่ได้ จากนั้นจึงเก็บค่าในฐานข้อมูลแบบ hash ไม่ควรเก็บค่า raw token ตรง ๆ
หลักการนี้คล้ายกับการเก็บรหัสผ่าน เพราะหากฐานข้อมูลรั่วไหล ผู้โจมตีก็จะไม่สามารถนำ token เดิมไปใช้งานได้ทันที
2) การเก็บ token ในฝั่งเบราว์เซอร์
ฝั่งผู้ใช้ควรเก็บ token ไว้ใน Cookie พร้อมตั้งค่าความปลอดภัยดังนี้
- HttpOnly: ป้องกัน JavaScript อ่านค่า cookie โดยตรง ลดความเสี่ยงจาก XSS
- Secure: ส่ง cookie ผ่าน HTTPS เท่านั้น
- SameSite: ช่วยลดความเสี่ยงจาก CSRF
- Max-Age: กำหนดอายุ เช่น 14 วัน หรือ 30 วัน ตามนโยบายของระบบ
การใช้ Cookie แบบนี้เหมาะกว่าการเก็บลง LocalStorage เพราะสามารถตั้งข้อจำกัดด้านความปลอดภัยได้ชัดเจนกว่า
3) การกลับมาใช้งานอีกครั้ง
เมื่อ session ปกติหมดอายุ แต่ cookie สำหรับ remember me ยังอยู่ เซิร์ฟเวอร์ควรตรวจสอบ token กับข้อมูลในฐานข้อมูล หากตรงกันจึงสร้าง session ใหม่ให้ผู้ใช้โดยอัตโนมัติ
อย่างไรก็ตาม จุดสำคัญมากคือ ต้องหมุน token ทันทีหลังใช้งานสำเร็จ เพื่อให้ token เดิมไม่สามารถถูกนำกลับมาใช้ซ้ำได้ หาก token หลุดออกไป ผู้โจมตีก็จะมีโอกาสใช้งานได้เพียงครั้งเดียวเท่านั้น
4) การออกจากระบบ
เมื่อผู้ใช้กด Logout ระบบควรทำครบทั้ง 3 ขั้นตอน ได้แก่
- ลบ session ปัจจุบัน
- ลบ remember token จากฐานข้อมูล
- สั่งให้เบราว์เซอร์ลบ cookie ทิ้ง
หากออกแบบดี ผู้ใช้จะสามารถออกจากระบบได้จริง และเพิกถอนการจดจำอุปกรณ์ที่เกี่ยวข้องได้อย่างชัดเจน
โครงสร้างข้อมูลในฐานข้อมูลที่นิยมใช้
โดยทั่วไปอาจมีตาราง เช่น user_remember_tokens เพื่อเก็บข้อมูลการจำอุปกรณ์ โดยมักประกอบด้วยฟิลด์สำคัญ เช่น
iduser_idtoken_hashdevice_nameหรือuser_agentcreated_atlast_used_atexpires_atrevoked_at
โครงสร้างลักษณะนี้ช่วยให้ระบบติดตามได้ว่า token ถูกสร้างเมื่อไร ใช้ล่าสุดเมื่อไร หมดอายุเมื่อไร และถูกเพิกถอนแล้วหรือยัง รวมถึงช่วยให้ผู้ใช้ดูรายการอุปกรณ์ที่เคยถูกจดจำได้ด้วย
เทคนิค selector + validator ที่ช่วยเพิ่มความปลอดภัย
อีกแนวทางที่นิยมมากคือการแยก token ออกเป็นสองส่วน ได้แก่
- selector: ค่าสั้น ใช้ค้นหา record ในฐานข้อมูลได้เร็ว
- validator: ค่ายาว ใช้เป็นตัวพิสูจน์ตัวจริง และควรเก็บในฐานข้อมูลแบบ hash
ตัวอย่างเช่น เซิร์ฟเวอร์สร้าง cookie ในรูปแบบ selector.validator แล้วเก็บ selector กับ hash(validator) ลงฐานข้อมูล
เมื่อผู้ใช้กลับมา ระบบจะ
- อ่านค่า
selector.validatorจาก cookie - ใช้ selector หา record ที่ตรงกัน
- นำ validator ไป hash แล้วเปรียบเทียบกับ
token_hash - หากตรงกัน ให้สร้าง session ใหม่
- หมุน token ชุดใหม่และอัปเดตฐานข้อมูลทันที
แนวทางนี้ช่วยลดผลกระทบหากฐานข้อมูลรั่ว เพราะผู้โจมตีจะไม่ได้ validator แบบ raw ไปใช้งานโดยตรง
ตัวอย่างค่าคุกกี้ที่แนะนำ
สำหรับการตั้งค่า cookie ของ Remember Me แนวคิดที่นิยมคือ
- Name:
remember - HttpOnly:
true - Secure:
true - SameSite:
LaxหรือStrictหากไม่กระทบ flow การล็อกอิน - Path:
/ - Max-Age:
1209600(14 วัน) หรือ2592000(30 วัน)
ค่าที่เหมาะสมควรขึ้นอยู่กับนโยบายของระบบ ประเภทข้อมูลที่ให้บริการ และระดับความเสี่ยงที่องค์กรยอมรับได้
ถ้าใช้ JWT อยู่แล้ว ควรออกแบบอย่างไร
สำหรับระบบที่ใช้ JWT อยู่แล้ว ไม่ควรนำ access token อายุยาว มาใช้แทน Remember Me เพราะ access token ควรมีอายุสั้นเพื่อลดผลกระทบหากรั่วไหล
แนวทางที่เหมาะสมคือใช้
- access token อายุสั้น สำหรับการเข้าถึง API
- refresh token อายุยาวกว่า สำหรับขอ access token ใหม่
ในบริบทนี้ Remember Me ควรทำหน้าที่เป็นเงื่อนไขที่อนุญาตให้มี refresh token ที่อายุยาวขึ้น และ refresh token ก็ควรถูกเก็บใน HttpOnly cookie เช่นกัน
ความเสี่ยงที่ยังต้องระวัง แม้ใช้ HttpOnly
แม้ HttpOnly จะช่วยป้องกัน JavaScript อ่าน cookie ไม่ได้ แต่ไม่ได้หมายความว่าปลอดภัยจาก XSS อย่างสมบูรณ์ เพราะหากเว็บมีช่องโหว่ XSS ผู้โจมตียังอาจส่ง request ในนามผู้ใช้ได้
ดังนั้น ระบบที่ปลอดภัยควรเสริมด้วยมาตรการอื่น เช่น
- ใช้ CSP (Content Security Policy)
- sanitize input อย่างรอบคอบ
- ระวังการ render HTML ที่มาจากผู้ใช้
- ตรวจสอบ flow สำคัญที่อาจถูกโจมตีร่วมกับ CSRF หรือ XSS
Remember Me ที่ปลอดภัยจึงไม่ใช่แค่เรื่องของ cookie อย่างเดียว แต่เป็นส่วนหนึ่งของการออกแบบความปลอดภัยทั้งระบบ
UX ที่ดีช่วยให้ทั้งปลอดภัยและใช้งานง่าย
นอกจากมุมเทคนิคแล้ว ประสบการณ์ผู้ใช้ก็สำคัญเช่นกัน แนวทางที่ดี ได้แก่
- แสดงข้อความชัดเจน เช่น “จำเครื่องนี้ 30 วัน”
- ให้ผู้ใช้ดูรายการอุปกรณ์ที่เคยถูกจดจำ
- มีปุ่มยกเลิกการจดจำรายอุปกรณ์
- แจ้งเตือนทางอีเมลเมื่อมีการเข้าสู่ระบบจากอุปกรณ์ใหม่
ฟีเจอร์เหล่านี้ช่วยเพิ่มความโปร่งใส ทำให้ผู้ใช้ควบคุมบัญชีของตนเองได้ดีขึ้น และช่วยตรวจจับกิจกรรมผิดปกติได้เร็วขึ้น
สรุป
Remember Me ที่ปลอดภัยไม่ควรสร้างจากการเก็บรหัสผ่านหรือโทเคนที่ไม่มีวันหมดอายุ แต่ควรออกแบบด้วย session + persistent token ที่มีอายุจำกัด เก็บใน HttpOnly Secure Cookie และตรวจสอบฝั่งเซิร์ฟเวอร์ผ่านค่าที่ hash ไว้ในฐานข้อมูล
หัวใจสำคัญที่ควรจำมี 3 ข้อคือ
- ไม่เก็บรหัสผ่าน
- ใช้ token ที่หมุนได้
- เพิกถอนได้เสมอ
หากทำครบตามนี้ ระบบจะได้ทั้งความสะดวกสำหรับผู้ใช้และความปลอดภัยในระดับที่เหมาะสมสำหรับงานจริง