Authentication & JWT Token
บทความนี้จะพาคุณไปทำความเข้าใจเกี่ยวกับการทำ Authentication และ JWT Token ตั้งแต่ความเป็นมา ความหมายของ Authentication และ JWT รวมถึงตัวอย่างการทำ Authentication & JWT Token ด้วย Go Fiber
Authentication คืออะไร
Authentication หรือ การพิสูจน์ตัวตน เป็นกระบวนการที่ระบบหรือแอปพลิเคชันใช้เพื่อตรวจสอบว่าผู้ใช้งานหรือระบบที่ร้องขอการเข้าถึงทรัพยากรนั้นเป็นใคร และมีสิทธิ์ในการเข้าถึงทรัพยากรหรือข้อมูลที่ร้องขอหรือไม่
Authentication เป็นขั้นตอนแรกที่สำคัญในกระบวนการรักษาความปลอดภัยของระบบ โดยมักใช้ควบคู่กับ Authorization (การอนุญาต) ซึ่งเป็นขั้นตอนที่ตรวจสอบว่าผู้ใช้นั้นมีสิทธิ์ทำอะไรได้บ้างในระบบ
ประเภทของ Authentication
-
การใช้สิ่งที่คุณรู้ (Something You Know) เป็นการพิสูจน์ตัวตนโดยใช้ข้อมูลที่ผู้ใช้งานรู้ เช่น
- รหัสผ่าน (Password)
- รหัส PIN
- คำถามรักษาความปลอดภัย (Security Questions)
-
การใช้สิ่งที่คุณมี (Something You Have) ใช้อุปกรณ์หรือสิ่งที่ผู้ใช้มี เช่น
- บัตรประชาชน/บัตรสมาร์ทการ์ด (Smart Card)
- อุปกรณ์ Token
- รหัส OTP (One-Time Password) ที่ส่งมาทาง SMS หรือแอปพลิเคชัน
-
การใช้สิ่งที่คุณเป็น (Something You Are) ใช้ข้อมูลทางชีวภาพ (Biometric) ของผู้ใช้ เช่น
- ลายนิ้วมือ (Fingerprint)
- การสแกนม่านตา (Iris Scan)
- ใบหน้า (Facial Recognition)
- เสียง (Voice Recognition)
JWT (JSON Web Token) คืออะไร?
JSON Web Token คือรูปแบบของโทเค็นที่ใช้สำหรับการส่งข้อมูลที่ปลอดภัยระหว่างฝ่ายต่าง ๆ ในรูปแบบ JSON โดยโทเค็นนี้มีโครงสร้างแบบเข้ารหัส ทำให้มั่นใจได้ว่าข้อมูลไม่ถูกแก้ไขระหว่างทาง JWT มักถูกใช้ในการพิสูจน์ตัวตน (Authentication) และการอนุญาต (Authorization) ในระบบที่มีการสื่อสารระหว่างเซิร์ฟเวอร์และไคลเอนต์
ในยุคแรกๆ การพัฒนาเว็บไซต์หรือแอปพลิเคชัน การจัดการผู้ใช้งานมักทำโดยการเก็บข้อมูลผู้ใช้ไว้ใน Session บน Server ซึ่งมีข้อจำกัดในเรื่องของ Scalability เมื่อมีผู้ใช้งานมากขึ้น Server ต้องแบกรับ Session จำนวนมาก ทำให้ประสิทธิภาพลดลง และเมื่อมีการใช้งานระบบแบบ Distributed System หรือ Microservices การจัดการ Session ยิ่งซับซ้อนมากขึ้น
โครงสร้างของ JWT
JWT มีรูปแบบที่แบ่งออกเป็น 3 ส่วนหลัก
- Header ใช้กำหนดข้อมูลเกี่ยวกับโทเค็น เช่น ประเภทของโทเค็น (JWT) และอัลกอริทึมที่ใช้ในการเข้ารหัส (เช่น HMAC, RSA)
- Payload คือข้อมูลหรือ Claims ที่ต้องการส่งไปยังอีกฝ่าย สามารถแบ่งเป็น
- Registered Claims ค่า Claim ที่กำหนดโดยมาตรฐาน เช่น iss (issuer), exp (expiration), sub (subject) เป็นต้น
- Public Claims ค่า Claim ที่ผู้พัฒนาระบุได้เอง เช่น userId, role
- Private Claims ค่า Claim ที่กำหนดโดยทั้งสองฝ่าย (ไคลเอนต์และเซิร์ฟเวอร์)
- Signature โดยเซิร์ฟเวอร์จะสร้างลายเซ็น(secretKey) เพื่อใช้ยืนยันว่าโทเค็นไม่ถูกแก้ไขระหว่างทาง
ตัวอย่าง JWT ที่สมบูรณ์
{ "header": { "alg": "HS256", "typ": "JWT" }, "payload": { "sub": "1234567890", "name": "John Doe", "admin": true }, "signature": "HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secretKey )" }
ข้อดีของ JWT
- Self-contained ข้อมูลทั้งหมดที่จำเป็นอยู่ในโทเค็นเดียว ทำให้ไม่ต้องอ้างอิงข้อมูลจากเซิร์ฟเวอร์เพิ่มเติม
- ง่ายต่อการใช้ในระบบกระจาย JWT เหมาะสำหรับระบบที่มีการใช้งานหลายเซิร์ฟเวอร์หรือ Microservices เพราะข้อมูลในโทเค็นเพียงพอต่อการตรวจสอบสิทธิ์
- ลดภาระเซิร์ฟเวอร์ เซิร์ฟเวอร์ไม่ต้องเก็บสถานะของผู้ใช้งาน (Stateless) เช่น ข้อมูล Session เพราะ JWT เก็บทุกอย่างไว้ในตัวมันเอง
- ปลอดภัยเมื่อใช้งาน HTTPS หากโทเค็นถูกส่งผ่าน HTTPS การป้องกันข้อมูลจะยิ่งปลอดภัยมากขึ้น
ข้อจำกัดและความเสี่ยง
- ความปลอดภัยของ Secret Key หาก Secret Key ที่ใช้สร้าง Signature รั่วไหล โทเค็นสามารถถูกปลอมแปลงได้
- ความยาวของโทเค็น เนื่องจากโทเค็นเก็บข้อมูลในรูปแบบ Base64 จึงอาจมีขนาดใหญ่ และอาจกระทบประสิทธิภาพเมื่อใช้ในคำขอ HTTP
- การหมดอายุ (Expiration) หากไม่กำหนดเวลาหมดอายุ (exp) อย่างเหมาะสม โทเค็นอาจถูกใช้งานเกินเวลาที่ควร
- ไม่สามารถเพิกถอนโทเค็น (Revoke) ได้ง่าย เนื่องจาก JWT เป็น Stateless การยกเลิกโทเค็นที่ถูกออกไปแล้วเป็นเรื่องยุ่งยาก ต้องใช้วิธีการเพิ่มเติม เช่น การเก็บรายการ "Blacklist" บนเซิร์ฟเวอร์
ก่อนที่จะเริ่มลงมือทำโปรเจ็ค มาดู Diagram ของการทำ Authentication กันก่อน
จาก Diagram จะแสดงกระบวนการทำงานของ JWT Authentication ในระบบที่มีการใช้ OpenID และ Identity Provider (IdP) เพื่อจัดการการพิสูจน์ตัวตน (Authentication) และการอนุญาต (Authorization)
ตัวอย่างการทำ Authentication และ Authorization ด้วย Go โดยใช้ Fiber Framework และ JWT Token
การทำงานโดยรวม
- Login Route (/api/v1/login) รับ Username และ Password จาก Body และตรวจสอบความถูกต้องของข้อมูล ถ้าข้อมูลถูกต้อง จะสร้าง JWT Token พร้อมข้อมูลผู้ใช้ (sub) เวลาหมดอายุ (exp) และแนบกลับไป ผ่าน Cookie ที่ชื่อว่า auth
- Protected Routes (/api/home) การเข้าถึงข้อมูลที่ได้รับการป้องกัน โดยจะมี middleware (Decode) จะทำการตรวจสอบ Token ที่แนบมาพร้อมกับ cookie หาก token ถูกต้อง จะส่งข้อความกลับไป “Welcome to the home route” พร้อม userID
เพิ่มเติม
- Configurable Secret Key ควรใช้ os.Getenv หรือ Environment Variables เก็บ jwtSecret แทนการเขียน Hardcoded
- Database Integration เชื่อมต่อกับฐานข้อมูลแทนการตรวจสอบ Username/Password แบบ Hardcoded
- เพิ่มระบบ Refresh Token เพื่อลดการใช้ Access Token ที่หมดอายุ
สรุปส่งท้าย
การทำ Authentication เป็นส่วนสำคัญที่ช่วยปกป้องระบบและข้อมูลของเราให้ปลอดภัยจากการเข้าถึงโดยไม่ได้รับอนุญาต อยากแนะนำให้ทุกคนศึกษาประเภทของ Authentication อย่างลึกซึ้ง เพื่อเพิ่มความมั่นคงให้ซอฟต์แวร์ของเรา หวังว่าบทความนี้จะช่วยให้คุณเข้าใจการยืนยันตัวตนและนำไปปรับใช้ได้อย่างเหมาะสมนะครับ