ในโลกดิจิทัลที่ขับเคลื่อนด้วยแอปพลิเคชันและบริการออนไลน์ การยืนยันตัวตน (Authentication) และการอนุญาต (Authorization) คือหัวใจสำคัญที่ไม่สามารถมองข้ามได้ครับ ไม่ว่าคุณจะเป็นผู้พัฒนาซอฟต์แวร์, เจ้าของธุรกิจดิจิทัล หรือแม้แต่ผู้ใช้งานทั่วไป การทำความเข้าใจว่าข้อมูลของคุณได้รับการปกป้องและจัดการอย่างไรจึงเป็นเรื่องที่จำเป็นอย่างยิ่ง ในบทความนี้ เราจะพาทุกท่านดำดิ่งสู่สองเทคโนโลยีหลักที่เข้ามาพลิกโฉมภูมิทัศน์ความปลอดภัยของเว็บแอปพลิเคชันและ API นั่นคือ OAuth 2.0 และ JWT (JSON Web Token) เราจะมาดูกันว่าเทคโนโลยีทั้งสองนี้คืออะไร ทำงานอย่างไร และเมื่อนำมาผสานรวมกันแล้ว จะสร้างระบบที่แข็งแกร่งและยืดหยุ่นได้อย่างไรบ้าง เพื่อให้คุณพร้อมที่จะนำไปประยุกต์ใช้ในโครงการของคุณได้อย่างมั่นใจครับ
บทความนี้จะให้ข้อมูลเชิงลึกตั้งแต่พื้นฐานไปจนถึงการใช้งานจริง พร้อมตัวอย่างโค้ดและข้อควรระวังด้านความปลอดภัย เพื่อให้คุณมีคู่มือที่ครบถ้วนสำหรับการสร้างระบบยืนยันตัวตนและการอนุญาตที่ทันสมัยและปลอดภัยครับ
- บทนำ: ความท้าทายของการยืนยันตัวตนและการอนุญาตในยุคดิจิทัล
- เจาะลึก OAuth 2.0: มาตรฐานสำหรับการอนุญาตที่ปลอดภัย
- เจาะลึก JWT (JSON Web Token): ตั๋วเดินทางที่เชื่อถือได้
- การผสานรวม OAuth 2.0 และ JWT Authentication: พลังที่สมบูรณ์แบบ
- ตัวอย่าง Code Snippets และการนำไปใช้งานจริง
- ข้อดีและข้อเสียของการใช้ OAuth 2.0 และ JWT ร่วมกัน
- คำถามที่พบบ่อย (FAQ)
- สรุปและ Call-to-Action
บทนำ: ความท้าทายของการยืนยันตัวตนและการอนุญาตในยุคดิจิทัล
ในอดีต การยืนยันตัวตนผู้ใช้งานมักจะเป็นไปในลักษณะที่ตรงไปตรงมาครับ นั่นคือผู้ใช้งานกรอก Username และ Password ซึ่งระบบจะนำไปตรวจสอบกับฐานข้อมูลโดยตรง หากถูกต้องก็อนุญาตให้เข้าถึง แต่เมื่อโลกของเว็บแอปพลิเคชันพัฒนาไปสู่ความซับซ้อนมากขึ้น มีการเชื่อมโยงกับบริการภายนอกหลากหลาย (เช่น การเข้าสู่ระบบด้วย Google หรือ Facebook) และมีการใช้งาน API เพื่อให้แอปพลิเคชันต่างๆ สื่อสารกันได้ ปัญหาเริ่มเกิดขึ้นครับ
ลองจินตนาการว่าคุณต้องการให้แอปพลิเคชันรูปภาพที่คุณพัฒนาขึ้น สามารถเข้าถึงรูปภาพใน Google Photos ของผู้ใช้งานได้ หากไม่มีมาตรฐานที่เหมาะสม ผู้ใช้งานอาจจะต้องกรอก Username และ Password ของ Google ให้กับแอปพลิเคชันรูปภาพของคุณ ซึ่งนั่นหมายความว่าแอปพลิเคชันของคุณจะมีสิทธิ์เข้าถึงบัญชี Google ของผู้ใช้งานได้อย่างเต็มที่ ไม่ใช่แค่รูปภาพเท่านั้น และที่สำคัญกว่านั้นคือ คุณจะต้องรับผิดชอบในการจัดเก็บ Username และ Password เหล่านั้นอย่างปลอดภัย ซึ่งเป็นภาระและความเสี่ยงที่สูงมากครับ
นี่คือจุดที่ OAuth 2.0 และ JWT เข้ามามีบทบาทสำคัญครับ พวกมันช่วยให้เราสามารถสร้างระบบที่ผู้ใช้งานสามารถอนุญาตให้แอปพลิเคชันหนึ่งเข้าถึงข้อมูลของตนในอีกบริการหนึ่งได้ โดยไม่ต้องเปิดเผยข้อมูลรับรองตัวตน (Username/Password) และยังสามารถส่งข้อมูลที่เชื่อถือได้ระหว่างบริการต่างๆ ได้อย่างมีประสิทธิภาพและปลอดภัย บทความนี้จะเจาะลึกทั้งสองเทคโนโลยีนี้เพื่อไขข้อสงสัยและเตรียมความพร้อมให้คุณครับ
เจาะลึก OAuth 2.0: มาตรฐานสำหรับการอนุญาตที่ปลอดภัย
OAuth 2.0 คืออะไร?
OAuth 2.0 (Open Authorization 2.0) ไม่ใช่โปรโตคอลสำหรับการยืนยันตัวตน (Authentication) นะครับ แต่เป็น เฟรมเวิร์กสำหรับการอนุญาต (Authorization Framework) ที่ช่วยให้แอปพลิเคชัน (Client) สามารถเข้าถึงทรัพยากรที่ได้รับการป้องกัน (Protected Resources) ในบริการอื่น (Resource Server) ในนามของผู้ใช้งาน (Resource Owner) โดยที่แอปพลิเคชันนั้นไม่จำเป็นต้องรู้ Username และ Password ของผู้ใช้งานครับ
พูดง่ายๆ คือ OAuth 2.0 เป็นเหมือนระบบที่ให้กุญแจชั่วคราวที่มีสิทธิ์จำกัดแก่แอปพลิเคชัน เพื่อให้มันสามารถทำบางอย่างในบัญชีของคุณบนบริการอื่นได้ โดยที่คุณไม่จำเป็นต้องมอบกุญแจหลัก (Username/Password) ให้แอปพลิเคชันนั้นไปเลยครับ ตัวอย่างที่เห็นได้ชัดคือการที่คุณอนุญาตให้แอปพลิเคชัน A เข้าถึงรายชื่อเพื่อนใน Facebook ของคุณ หรือเข้าถึงไฟล์ใน Google Drive ของคุณ โดยที่คุณไม่ต้องกรอก Username/Password ของ Facebook หรือ Google ลงในแอปพลิเคชัน A โดยตรงครับ
บทบาทสำคัญใน OAuth 2.0 มี 4 ส่วนหลักๆ ครับ:
- Resource Owner (เจ้าของทรัพยากร): คือผู้ใช้งาน เช่น คุณเอง ที่เป็นเจ้าของข้อมูลหรือทรัพยากรต่างๆ (เช่น รูปภาพ, รายชื่อผู้ติดต่อ) ที่เก็บอยู่ในบริการอื่น
- Client (แอปพลิเคชัน): คือแอปพลิเคชันที่ต้องการเข้าถึงทรัพยากรของ Resource Owner เช่น แอปพลิเคชันรูปภาพ, แอปพลิเคชันส่งเสริมการขาย
- Authorization Server (เซิร์ฟเวอร์อนุญาต): เป็นบริการที่ออก Access Token ให้กับ Client หลังจากที่ Resource Owner ได้อนุญาตแล้ว มักจะเป็นส่วนหนึ่งของบริการที่ถือครองทรัพยากร
- Resource Server (เซิร์ฟเวอร์ทรัพยากร): เป็นบริการที่จัดเก็บทรัพยากรของ Resource Owner และสามารถให้ Client เข้าถึงได้เมื่อมี Access Token ที่ถูกต้อง
ทำไมต้องใช้ OAuth 2.0?
การใช้ OAuth 2.0 มีประโยชน์มากมายครับ:
- ความปลอดภัย: ผู้ใช้งานไม่จำเป็นต้องเปิดเผย Username และ Password ให้กับ Client ซึ่งช่วยลดความเสี่ยงที่ข้อมูลรับรองตัวตนจะถูกขโมยหรือนำไปใช้ในทางที่ผิด
- การควบคุมสิทธิ์ (Granular Control): Resource Owner สามารถกำหนดขอบเขตสิทธิ์ (Scope) ที่จะอนุญาตให้ Client เข้าถึงได้ เช่น อนุญาตให้อ่านข้อมูลโปรไฟล์เท่านั้น ไม่อนุญาตให้โพสต์
- ความสะดวกสบาย: ผู้ใช้งานไม่ต้องสร้างบัญชีใหม่สำหรับทุกแอปพลิเคชัน แต่สามารถใช้บัญชีที่มีอยู่แล้ว (เช่น Google, Facebook) ในการอนุญาตได้
- ลดภาระของ Client: Client ไม่ต้องรับผิดชอบในการจัดเก็บและปกป้องข้อมูลรับรองตัวตนของผู้ใช้งาน ลดความซับซ้อนด้านความปลอดภัย
- มาตรฐานอุตสาหกรรม: เป็นมาตรฐานที่ได้รับการยอมรับและใช้งานอย่างกว้างขวาง ทำให้เกิดความเข้ากันได้ระหว่างบริการต่างๆ
หลักการทำงานของ OAuth 2.0 (ภาพรวม)
โดยหลักการแล้ว การทำงานของ OAuth 2.0 จะมีขั้นตอนประมาณนี้ครับ:
- Client ต้องการเข้าถึงทรัพยากรบน Resource Server ในนามของ Resource Owner
- Client จะนำ Resource Owner ไปยัง Authorization Server เพื่อขออนุญาต
- Authorization Server จะแสดงหน้าจอให้ Resource Owner ยืนยันว่าต้องการอนุญาตให้ Client เข้าถึงทรัพยากรในขอบเขตที่ร้องขอหรือไม่
- หาก Resource Owner อนุญาต Authorization Server จะส่ง Authorization Grant (เช่น Authorization Code) กลับไปยัง Client ผ่าน Redirect URI ที่ลงทะเบียนไว้
- Client จะใช้ Authorization Grant ที่ได้รับ แลกเปลี่ยนกับ Access Token ที่ Authorization Server
- Authorization Server จะตรวจสอบ Grant และหากถูกต้อง จะออก Access Token (และอาจรวมถึง Refresh Token) ให้กับ Client
- Client ใช้ Access Token นี้ในการร้องขอเข้าถึงทรัพยากรจาก Resource Server
- Resource Server ตรวจสอบ Access Token และหากถูกต้อง ก็จะให้ Client เข้าถึงทรัพยากรที่ร้องขอ
กระบวนการนี้จะแตกต่างกันไปเล็กน้อยขึ้นอยู่กับ “Grant Type” ที่เลือกใช้นะครับ ซึ่งเราจะมาดูกันในหัวข้อถัดไปครับ
Grant Types (Flows) ต่างๆ ของ OAuth 2.0
OAuth 2.0 มีหลาย Grant Type หรือ “Flows” ที่ออกแบบมาเพื่อรองรับสถานการณ์การใช้งานที่แตกต่างกันครับ แต่ละแบบก็มีข้อดีข้อเสียและระดับความปลอดภัยที่ต่างกันไป:
1. Authorization Code Grant
นี่คือ Grant Type ที่นิยมและปลอดภัยที่สุดสำหรับแอปพลิเคชันเว็บแบบดั้งเดิม (Web Applications) และ Single-Page Applications (SPA) ที่มี Backend เป็นของตัวเองครับ
- ขั้นตอน:
- Client (แอปพลิเคชันของคุณ) ส่งผู้ใช้งานไปยัง Authorization Server เพื่อขออนุญาตพร้อมระบุ
scope,client_id, และredirect_uri - Authorization Server แสดงหน้ายินยอมแก่ผู้ใช้งาน
- ผู้ใช้งานอนุญาต
- Authorization Server ส่ง
authorization_codeกลับไปยังredirect_uriของ Client - Client (ฝั่ง Backend) แลกเปลี่ยน
authorization_codeกับaccess_tokenและrefresh_tokenโดยส่งclient_secretไปด้วย (ซึ่งclient_secretจะปลอดภัยเพราะอยู่ฝั่ง Backend) - Authorization Server ตรวจสอบและส่ง
access_tokenและrefresh_tokenให้ Client - Client ใช้
access_tokenในการเรียกใช้ Resource Server
- Client (แอปพลิเคชันของคุณ) ส่งผู้ใช้งานไปยัง Authorization Server เพื่อขออนุญาตพร้อมระบุ
- ความปลอดภัย: สูง เพราะ
authorization_codeมีอายุสั้นและสามารถแลกเป็น Access Token ได้เพียงครั้งเดียว และclient_secretจะไม่ถูกเปิดเผยต่อผู้ใช้งาน - การใช้งาน: Web applications, SPAs (โดยเฉพาะเมื่อใช้ร่วมกับ PKCE)
เพื่อให้ปลอดภัยยิ่งขึ้นสำหรับ Single-Page Applications หรือ Mobile Apps ที่ไม่มี Backend ที่ปลอดภัยสำหรับเก็บ client_secret ควรใช้ Proof Key for Code Exchange (PKCE) ร่วมด้วยครับ อ่านเพิ่มเติมเกี่ยวกับ PKCE
2. Implicit Grant (Deprecated in OAuth 2.1)
Implicit Grant เคยเป็นที่นิยมสำหรับ SPAs และ Mobile Apps แต่ปัจจุบันถือว่าไม่ปลอดภัยและไม่แนะนำให้ใช้แล้วครับ (ถูกถอดออกจาก OAuth 2.1)
- ขั้นตอน:
- Client ส่งผู้ใช้งานไปยัง Authorization Server เพื่อขออนุญาตพร้อมระบุ
scope,client_id, และredirect_uri - ผู้ใช้งานอนุญาต
- Authorization Server ส่ง
access_tokenกลับไปยังredirect_uriของ Client โดยตรง (ผ่าน URL fragment) - Client ใช้
access_tokenในการเรียกใช้ Resource Server
- Client ส่งผู้ใช้งานไปยัง Authorization Server เพื่อขออนุญาตพร้อมระบุ
- ความปลอดภัย: ต่ำ เนื่องจาก
access_tokenถูกส่งผ่าน URL fragment ซึ่งอาจถูกดักจับโดยเบราว์เซอร์, ประวัติการเข้าชม หรือมัลแวร์ได้ง่าย - การใช้งาน: ไม่แนะนำให้ใช้แล้ว ให้หันไปใช้ Authorization Code Grant with PKCE แทน
3. Resource Owner Password Credentials Grant
Grant Type นี้ให้ Client ร้องขอ access_token โดยตรงโดยใช้ Username และ Password ของ Resource Owner ครับ
- ขั้นตอน:
- Client ร้องขอ Username และ Password จากผู้ใช้งาน
- Client ส่ง Username, Password,
client_id,client_secretไปยัง Authorization Server - Authorization Server ตรวจสอบข้อมูลและออก
access_tokenและrefresh_tokenให้ Client - Client ใช้
access_tokenในการเรียกใช้ Resource Server
- ความปลอดภัย: ต่ำมาก และไม่แนะนำให้ใช้ ยกเว้นในกรณีที่ Client เป็นแอปพลิเคชันที่เชื่อถือได้สูงของ Resource Owner เอง (เช่น แอปพลิเคชันของบริษัทสำหรับพนักงาน) เพราะ Client จะต้องเก็บ Username และ Password ของผู้ใช้งานไว้
- การใช้งาน: ไม่แนะนำในสถานการณ์ทั่วไป
4. Client Credentials Grant
Grant Type นี้ใช้สำหรับ Client ที่ต้องการเข้าถึงทรัพยากรของตนเอง ไม่ใช่ในนามของผู้ใช้งาน โดยตรงระหว่าง Server-to-Server ครับ
- ขั้นตอน:
- Client (ซึ่งในที่นี้คือ Server ของคุณ) ส่ง
client_idและclient_secretไปยัง Authorization Server - Authorization Server ตรวจสอบข้อมูลและออก
access_tokenให้ Client - Client ใช้
access_tokenในการเรียกใช้ Resource Server
- Client (ซึ่งในที่นี้คือ Server ของคุณ) ส่ง
- ความปลอดภัย: เหมาะสมสำหรับการใช้งาน Server-to-Server เนื่องจาก
client_secretสามารถเก็บไว้ได้อย่างปลอดภัยบน Server - การใช้งาน: Microservices, Background services ที่ต้องการเข้าถึง API อื่นๆ
Refresh Token
Refresh Token เป็น Token พิเศษที่ Authorization Server ออกให้พร้อมกับ Access Token ครับ โดยมีวัตถุประสงค์เพื่อใช้ในการขอ Access Token ใหม่ เมื่อ Access Token เดิมหมดอายุแล้ว โดยไม่ต้องให้ผู้ใช้งานต้องกลับไปยืนยันตัวตนซ้ำอีกครั้งครับ
- ความสำคัญ:
- ช่วยเพิ่มความปลอดภัยโดยการทำให้ Access Token มีอายุสั้น
- ช่วยให้ผู้ใช้งานได้รับประสบการณ์ที่ราบรื่น ไม่ต้อง Login บ่อยๆ
- การใช้งาน:
- Client เก็บ Refresh Token ไว้ (ควรเก็บอย่างปลอดภัย เช่น ใน HttpOnly Cookie)
- เมื่อ Access Token หมดอายุ Client จะส่ง Refresh Token ไปยัง Authorization Server เพื่อขอ Access Token ใหม่
- Authorization Server ตรวจสอบ Refresh Token และออก Access Token ใหม่ (อาจจะออก Refresh Token ใหม่ให้ด้วยก็ได้)
- ข้อควรระวัง: Refresh Token มีอายุยาวนานกว่า Access Token จึงต้องได้รับการปกป้องอย่างเข้มงวด หาก Refresh Token รั่วไหล ผู้โจมตีอาจนำไปใช้ในการขอ Access Token ใหม่ได้เรื่อยๆ ครับ
ข้อควรระวังและการรักษาความปลอดภัยในการใช้งาน OAuth 2.0
แม้ OAuth 2.0 จะถูกออกแบบมาเพื่อความปลอดภัย แต่ก็มีข้อควรระวังและแนวทางปฏิบัติที่ดีที่สุดที่คุณต้องพิจารณาครับ:
- ใช้ HTTPS เสมอ: การสื่อสารทั้งหมดระหว่าง Client, Authorization Server และ Resource Server ต้องเป็น HTTPS เพื่อป้องกันการดักฟังข้อมูล
- ตรวจสอบ Redirect URI อย่างเข้มงวด: Authorization Server ต้องตรวจสอบให้แน่ใจว่า
redirect_uriที่ส่งมาตรงกับที่ลงทะเบียนไว้ทุกประการ เพื่อป้องกันการโจมตีแบบ Open Redirect - ใช้ State Parameter: ส่ง
stateparameter ที่เป็นค่าสุ่มที่ไม่ซ้ำกันใน Authorization Request และตรวจสอบย้อนกลับใน Authorization Response เพื่อป้องกันการโจมตีแบบ CSRF (Cross-Site Request Forgery) - ใช้ PKCE (Proof Key for Code Exchange): สำหรับ Public Clients (เช่น SPAs, Mobile Apps) ให้ใช้ PKCE ร่วมกับ Authorization Code Grant เพื่อเพิ่มความปลอดภัยยิ่งขึ้น ศึกษาเพิ่มเติมเกี่ยวกับ PKCE
- จัดการ Client Secret อย่างปลอดภัย: สำหรับ Confidential Clients (ที่มี Backend) ต้องเก็บ
client_secretไว้ในที่ปลอดภัย และไม่เปิดเผยสู่สาธารณะ - กำหนด Scope ที่แคบที่สุด: ขอสิทธิ์ (Scope) เท่าที่จำเป็นเท่านั้น เพื่อจำกัดความเสียหายหาก Client ถูกบุกรุก
- กำหนดอายุของ Access Token และ Refresh Token: ให้ Access Token มีอายุสั้น และใช้ Refresh Token เพื่อขอ Access Token ใหม่ เพื่อลดช่วงเวลาที่ผู้โจมตีสามารถใช้ Access Token ที่ถูกขโมยไปได้
- Revoke Token เมื่อจำเป็น: มีกลไกในการเพิกถอน (Revoke) Access Token และ Refresh Token ในกรณีที่ถูกขโมย หรือผู้ใช้งานยกเลิกสิทธิ์
เจาะลึก JWT (JSON Web Token): ตั๋วเดินทางที่เชื่อถือได้
JWT คืออะไร?
JWT ย่อมาจาก JSON Web Token ครับ มันคือมาตรฐาน (RFC 7519) ที่ใช้สำหรับส่งข้อมูลอย่างปลอดภัยระหว่างผู้ส่งและผู้รับ โดยข้อมูลที่ส่งไปนั้นเป็น JSON และมีการลงชื่อดิจิทัล (Digitally Signed) ทำให้มั่นใจได้ว่าข้อมูลนั้นไม่ได้ถูกแก้ไขระหว่างทาง และมาจากแหล่งที่มาที่เชื่อถือได้ครับ
ลองนึกภาพว่า JWT คือ “ตั๋วเดินทาง” ที่มีข้อมูลเกี่ยวกับผู้ถือตั๋ว (เช่น ชื่อผู้ใช้งาน, สิทธิ์การเข้าถึง) และมีลายเซ็นรับรองความถูกต้องจากผู้ออกตั๋ว (Server) ครับ เมื่อผู้โดยสาร (Client) นำตั๋วนี้ไปแสดงที่จุดตรวจ (Resource Server) เจ้าหน้าที่ (Resource Server) ก็สามารถตรวจสอบลายเซ็นบนตั๋วได้ทันทีว่าตั๋วนี้เป็นของแท้ ไม่ได้ถูกปลอมแปลง หรือถูกแก้ไข และสามารถอ่านข้อมูลบนตั๋วเพื่อตัดสินใจอนุญาตให้ผู้โดยสารเข้าถึงบริการได้เลย โดยไม่ต้องไปสอบถามข้อมูลจากศูนย์กลาง (Authorization Server) ทุกครั้งครับ
JWT มักจะใช้เป็น Access Token ในระบบ OAuth 2.0 และ OpenID Connect ครับ
ทำไมต้องใช้ JWT?
JWT มีข้อดีหลายประการที่ทำให้เป็นที่นิยมในการใช้งาน โดยเฉพาะในสถาปัตยกรรมแบบ Microservices และ Stateless API ครับ
- Statelessness: Server ไม่จำเป็นต้องเก็บ Session State ของผู้ใช้งานไว้ ทำให้ระบบมีความยืดหยุ่นและปรับขนาดได้ง่าย (Scalable) เหมาะสำหรับ Load Balancer และ Microservices
- Scalability: เนื่องจาก Server ไม่ต้องเก็บสถานะ ทำให้สามารถเพิ่มจำนวน Server ได้ง่ายขึ้นโดยไม่ต้องกังวลเรื่องการซิงค์ Session
- Compactness: JWT มีขนาดเล็ก สามารถส่งผ่าน URL, POST parameter หรือ HTTP header ได้อย่างง่ายดาย
- Self-contained: JWT บรรจุข้อมูลที่จำเป็นทั้งหมดไว้ในตัวเอง ทำให้ Resource Server สามารถตรวจสอบความถูกต้องและอ่านข้อมูลได้ทันที โดยไม่ต้องไปสอบถามฐานข้อมูลหรือ Authorization Server เพิ่มเติม
- Security: มีการลงชื่อดิจิทัล ทำให้มั่นใจได้ว่าข้อมูลไม่ถูกแก้ไขและมาจากแหล่งที่เชื่อถือได้ (หากใช้คีย์ลับที่แข็งแกร่ง)
โครงสร้างของ JWT (Header, Payload, Signature)
JWT ประกอบด้วยสามส่วนหลักๆ ที่คั่นด้วยจุด (.) และถูกเข้ารหัสด้วย Base64Url-encoded ครับ:
Header.Payload.Signature
1. Header (ส่วนหัว)
เป็นส่วนแรกของ JWT ครับ มันเป็น JSON object ที่ระบุประเภทของ Token (typ) ซึ่งส่วนใหญ่จะเป็น “JWT” และระบุอัลกอริทึมที่ใช้ในการเข้ารหัสลายเซ็น (alg) เช่น HS256 (HMAC SHA256) หรือ RS256 (RSA SHA256)
{
"alg": "HS256",
"typ": "JWT"
}
หลังจากนั้นจะถูกเข้ารหัส Base64Url-encoded ครับ
2. Payload (ส่วนข้อมูล)
เป็นส่วนกลางของ JWT และเป็น JSON object ที่บรรจุ “Claims” ซึ่งเป็นข้อความที่ต้องการส่งต่อครับ Claims มี 3 ประเภทหลักๆ:
- Registered Claims: เป็น Claims ที่ถูกกำหนดไว้ล่วงหน้าโดยมาตรฐาน เพื่อให้สามารถทำงานร่วมกันได้ (ไม่บังคับใช้)
iss(issuer): ผู้ออก Tokensub(subject): หัวข้อของ Token (มักจะเป็น User ID)aud(audience): ผู้รับ Tokenexp(expiration time): เวลาที่ Token หมดอายุ (เป็น Unix timestamp)nbf(not before): เวลาที่ Token เริ่มใช้งานได้iat(issued at): เวลาที่ Token ถูกออกjti(JWT ID): ID เฉพาะของ Token
- Public Claims: เป็น Claims ที่เราสามารถกำหนดเองได้ แต่ต้องจดทะเบียนใน IANA JSON Web Token Registry เพื่อป้องกันความขัดแย้ง
- Private Claims: เป็น Claims ที่เรากำหนดเองได้ตามความต้องการของแอปพลิเคชัน โดยไม่ต้องจดทะเบียน แต่ต้องระมัดระวังไม่ให้ชื่อ Claim ไปซ้ำกับ Registered หรือ Public Claims ครับ (เช่น
"role": "admin","companyId": "123")
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622,
"isAdmin": true
}
หลังจากนั้นจะถูกเข้ารหัส Base64Url-encoded ครับ
ข้อควรจำ: Payload ไม่ได้ถูกเข้ารหัส (Encrypted) นะครับ แต่ถูกเข้ารหัสแบบ Base64Url-encoded เท่านั้น ซึ่งสามารถถอดรหัสกลับมาอ่านได้ง่าย ดังนั้น ห้ามใส่ข้อมูลที่เป็นความลับสูงลงใน Payload โดยเด็ดขาดครับ
3. Signature (ส่วนลายเซ็น)
เป็นส่วนที่สำคัญที่สุดของ JWT ในแง่ของความปลอดภัยครับ ลายเซ็นนี้ใช้เพื่อยืนยันว่า Token ไม่ได้ถูกแก้ไขระหว่างทาง และมาจากผู้ออกที่ถูกต้อง
การสร้าง Signature ทำได้โดยการนำ Base64Url-encoded Header, เครื่องหมายจุด (.), และ Base64Url-encoded Payload มาต่อกัน จากนั้นนำสตริงที่ได้ไปเข้ากระบวนการเข้ารหัสด้วยอัลกอริทึมที่ระบุใน Header (เช่น HS256) พร้อมกับ “Secret Key” หรือ Private Key ที่รู้กันเฉพาะผู้ออก Token เท่านั้นครับ
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
ผลลัพธ์ที่ได้คือ Signature ครับ
เมื่อรวมทั้งสามส่วนเข้าด้วยกัน เราก็จะได้ JWT ที่สมบูรณ์แบบนี้ครับ
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjIsImlzQWRtaW4iOnRydWV9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
ประเภทของ JWT
JWT โดยทั่วไปจะแบ่งออกเป็นสองประเภทหลักๆ ครับ:
- JWS (JSON Web Signature): เป็น JWT ที่เราพูดถึงไปข้างต้นครับ คือมีการลงชื่อดิจิทัลเพื่อรับรองความถูกต้องของข้อมูล (Integrity) และยืนยันแหล่งที่มา (Authenticity) แต่ข้อมูลใน Payload ไม่ได้ถูกเข้ารหัส
- JWE (JSON Web Encryption): เป็น JWT ที่มีการเข้ารหัสข้อมูลใน Payload ด้วยครับ นอกจากจะมีการลงชื่อดิจิทัลแล้ว ข้อมูลยังถูกเข้ารหัส ทำให้ไม่สามารถอ่านได้โดยผู้ที่ไม่มี Private Key หรือ Secret Key ที่ถูกต้อง เหมาะสำหรับกรณีที่ต้องการส่งข้อมูลที่เป็นความลับจริงๆ
ในบทความนี้ เราจะเน้นไปที่ JWS เป็นหลักนะครับ เนื่องจากเป็นรูปแบบที่นิยมใช้กันมากที่สุดในการเป็น Access Token ครับ
การใช้งาน JWT ในการยืนยันตัวตน (Authentication)
JWT ได้รับความนิยมอย่างมากในการนำมาใช้เป็น Access Token ในระบบการยืนยันตัวตนแบบ Stateless ครับ ซึ่งมีขั้นตอนการทำงานโดยสรุปดังนี้:
- Login: ผู้ใช้งานส่ง Username และ Password ไปยัง Authentication Server (ซึ่งอาจเป็น Authorization Server ในบริบทของ OAuth 2.0)
- Authentication Server: ตรวจสอบ Username และ Password หากถูกต้อง จะสร้าง JWT ที่มีข้อมูลของผู้ใช้งาน (เช่น User ID, Roles, Permissions) และลงชื่อด้วย Secret Key ของ Server นั้นๆ
- Return JWT: Authentication Server ส่ง JWT กลับคืนไปยัง Client (มักจะอยู่ในรูปของ Access Token)
- Subsequent Requests: เมื่อ Client ต้องการเข้าถึง Protected Resource บน Resource Server ในการร้องขอครั้งต่อๆ ไป Client จะแนบ JWT (Access Token) ไปใน HTTP Header, โดยปกติจะอยู่ในรูปแบบ
Authorization: Bearer <your_jwt_token> - Resource Server: เมื่อได้รับคำขอ Resource Server จะตรวจสอบ JWT นั้นๆ
- ตรวจสอบลายเซ็น (Signature) โดยใช้ Secret Key เดียวกันกับที่ Authentication Server ใช้ในการสร้าง เพื่อยืนยันว่า Token ไม่ได้ถูกปลอมแปลงหรือแก้ไข
- ตรวจสอบ Claims ต่างๆ เช่น
exp(วันหมดอายุ),iss(ผู้ออก),aud(ผู้รับ) และข้อมูลสิทธิ์ต่างๆ ใน Payload
- Authorization: หาก JWT ถูกต้องและยังไม่หมดอายุ Resource Server จะอนุญาตให้ Client เข้าถึงทรัพยากรตามสิทธิ์ที่ระบุใน Payload ได้เลย โดยไม่ต้องไปสอบถาม Authentication Server อีกครั้ง
ข้อควรระวังและการรักษาความปลอดภัยในการใช้งาน JWT
แม้ JWT จะมีข้อดีหลายประการ แต่ก็มีข้อควรระวังด้านความปลอดภัยที่คุณต้องทำความเข้าใจครับ:
- ปกป้อง Secret Key: Secret Key ที่ใช้ในการลงชื่อ JWT เป็นสิ่งสำคัญที่สุดครับ หากผู้โจมตีทราบ Secret Key พวกเขาสามารถสร้าง JWT ปลอมขึ้นมาได้ ซึ่งจะผ่านการตรวจสอบลายเซ็นได้ทั้งหมด ดังนั้น Secret Key ต้องเก็บไว้เป็นความลับสูงสุดบน Server เท่านั้น และไม่ควร hardcode ไว้ในโค้ด
- กำหนดวันหมดอายุ (Expiration –
expClaim): JWT ควรมีอายุสั้น เพื่อลดช่วงเวลาที่ผู้โจมตีสามารถใช้ Token ที่ถูกขโมยไปได้ และใช้ Refresh Token เพื่อขอ Access Token ใหม่ - การเพิกถอน Token (Revocation): เนื่องจาก JWT เป็น Stateless การเพิกถอน Token ที่ถูกออกไปแล้วจึงเป็นเรื่องที่ท้าทาย หาก Token ถูกขโมยและยังไม่หมดอายุ ผู้โจมตีสามารถใช้งานได้จนกว่าจะหมดอายุ แนวทางแก้ไขคือการสร้าง Blacklist (หรือ Revocation List) ของ Token ที่ถูกเพิกถอน หรือใช้วิธีเปลี่ยน Secret Key บ่อยๆ (ซึ่งอาจส่งผลกระทบต่อผู้ใช้งานอื่น)
- ห้ามเก็บข้อมูลความลับใน Payload: อย่างที่กล่าวไปแล้ว Payload ของ JWT ถูก Base64Url-encoded ซึ่งสามารถถอดรหัสและอ่านได้ง่าย ดังนั้นห้ามใส่ข้อมูลที่เป็นความลับสูง (เช่น Username, Password, เลขบัตรเครดิต) ลงใน Payload เด็ดขาดครับ ควรเก็บเพียงข้อมูลที่จำเป็นต่อการยืนยันตัวตนและการอนุญาตเท่านั้น
- ใช้ HTTPS เสมอ: การส่ง JWT ต้องทำผ่าน HTTPS เพื่อป้องกันการดักจับ Token ระหว่างการส่ง
- การจัดเก็บ JWT ใน Client: ควรเก็บ Access Token (ที่เป็น JWT) ไว้ในหน่วยความจำของแอปพลิเคชัน (in-memory) หรือ Local Storage/Session Storage ของเบราว์เซอร์อย่างระมัดระวัง เพื่อป้องกันการโจมตีแบบ XSS (Cross-Site Scripting) การเก็บใน HttpOnly Cookie อาจเป็นตัวเลือกที่ดีสำหรับป้องกัน XSS แต่ก็ยังมีช่องโหว่ต่อ CSRF หากไม่จัดการอย่างถูกต้อง
- ตรวจสอบ Claims ทั้งหมด: เมื่อ Resource Server ได้รับ JWT ต้องตรวจสอบ Claims ที่สำคัญทั้งหมด เช่น
exp,iss,aud,nbfเพื่อให้แน่ใจว่า Token ยังถูกต้องและเหมาะสมกับการใช้งาน
การผสานรวม OAuth 2.0 และ JWT Authentication: พลังที่สมบูรณ์แบบ
มาถึงส่วนสำคัญที่เราจะเห็นภาพรวมว่าเทคโนโลยีทั้งสองนี้ทำงานร่วมกันอย่างไรครับ OAuth 2.0 เป็นเฟรมเวิร์กสำหรับการอนุญาต ในขณะที่ JWT เป็นรูปแบบของ Token ที่ใช้ในการส่งข้อมูลอย่างปลอดภัย ซึ่งในระบบสมัยใหม่มักจะนำมาใช้ร่วมกัน โดย JWT จะถูกใช้เป็น Access Token ที่ OAuth 2.0 ออกให้ครับ
OAuth 2.0 vs. JWT: บทบาทที่แตกต่างแต่เติมเต็มกัน
เพื่อความเข้าใจที่ชัดเจน มาดูตารางเปรียบเทียบบทบาทหลักของทั้งสองเทคโนโลยีกันครับ:
| คุณสมบัติ | OAuth 2.0 | JWT (JSON Web Token) |
|---|---|---|
| วัตถุประสงค์หลัก | เฟรมเวิร์กสำหรับการ อนุญาต (Authorization) ให้ Client เข้าถึงทรัพยากรในนามของผู้ใช้งาน โดยไม่ต้องรู้ Username/Password | มาตรฐานสำหรับการส่งข้อมูลอย่างปลอดภัยและกระชับ โดยมีการ ลงชื่อ (Signature) เพื่อรับรองความถูกต้อง (มักใช้สำหรับการยืนยันตัวตนและข้อมูลสิทธิ์) |
| ประเภท | Authorization Framework (ชุดของโปรโตคอลและแนวคิด) | Token Format (รูปแบบของข้อมูล) |
| สิ่งที่ออกให้ | Access Token, Refresh Token, Authorization Code (หรือข้อมูล Grant อื่นๆ) | ข้อมูลที่ถูกลงชื่อดิจิทัล (Signed Token) มักใช้เป็น Access Token, ID Token |
| การจัดการสถานะ (State) | สามารถเป็นทั้ง Stateful (ต้องเก็บ Authorization Code) หรือ Stateless (เมื่อ Client ได้รับ Token แล้ว) | เป็น Stateless โดยธรรมชาติ (ข้อมูลทั้งหมดอยู่ใน Token) |
| การตรวจสอบ | Authorization Server ตรวจสอบ Grant และออก Token | Resource Server ตรวจสอบลายเซ็นและ Claim ใน Token |
| เกี่ยวข้องกับการยืนยันตัวตนหรือไม่ | ไม่ใช่โดยตรง แต่เป็นพื้นฐานให้ OpenID Connect ซึ่งเป็นโปรโตคอลยืนยันตัวตนที่สร้างบน OAuth 2.0 | ใช้ในการยืนยันตัวตน (Authentication) ได้ โดยเฉพาะในรูปแบบที่เรียกว่า “Bearer Token” |
| ใครใช้งาน | Client, Resource Owner, Authorization Server, Resource Server | Authentication Server (ผู้ออก), Client (ผู้ถือ), Resource Server (ผู้ตรวจสอบ) |
จะเห็นได้ว่า OAuth 2.0 เป็น “วิธี” ในการได้มาซึ่งสิทธิ์ในการเข้าถึง (Access) ส่วน JWT เป็น “รูปแบบ” ของ “บัตรผ่าน” หรือ “กุญแจ” ที่ใช้ในการเข้าถึงนั้นเองครับ พวกมันจึงทำงานร่วมกันได้อย่างลงตัว
Flow การทำงานเมื่อผสานรวมกัน
เมื่อ OAuth 2.0 ใช้ JWT เป็น Access Token การทำงานจะมีลักษณะดังนี้ครับ (โดยใช้ Authorization Code Grant เป็นตัวอย่าง)
- Client (แอปพลิเคชัน) เริ่มต้นการอนุญาต: ผู้ใช้งานต้องการให้แอปพลิเคชันเข้าถึงข้อมูลบางอย่างจาก Resource Server (เช่น Google Photos) แอปพลิเคชันจะนำผู้ใช้งานไปยัง Authorization Server ของ Google พร้อมระบุ
client_id,redirect_uri,scope(ขอบเขตสิทธิ์ที่ต้องการ) และstateparameter ครับ - ผู้ใช้งานยืนยันตัวตนและอนุญาต: Authorization Server ของ Google จะแสดงหน้าจอ Login ให้ผู้ใช้งาน หากยังไม่ได้ Login และแสดงข้อมูลว่าแอปพลิเคชันต้องการสิทธิ์อะไรบ้าง ผู้ใช้งานทำการ Login และกด “อนุญาต” ครับ
- Authorization Server ส่ง Authorization Code: หากผู้ใช้งานอนุญาต Authorization Server จะส่ง
authorization_codeกลับไปยังredirect_uriของแอปพลิเคชัน (Client) พร้อมกับstateparameter ที่ส่งไปในตอนแรก - Client แลกเปลี่ยน Code กับ Token: แอปพลิเคชัน (ฝั่ง Backend) จะรับ
authorization_codeมา จากนั้นส่งauthorization_code,client_id,client_secret(ถ้ามี) และredirect_uriไปยัง Authorization Server อีกครั้ง เพื่อขอแลกเปลี่ยนเป็น Token ครับ - Authorization Server ออก JWT (Access Token) และ Refresh Token: Authorization Server ตรวจสอบ
authorization_codeและข้อมูลอื่นๆ หากถูกต้อง จะสร้าง JWT (ซึ่งเป็น Access Token) ที่มีข้อมูลสิทธิ์ของผู้ใช้งาน และลงชื่อด้วย Secret Key ของตนเอง จากนั้นส่ง JWT นี้ (เป็น Access Token) พร้อมกับ Refresh Token กลับไปยังแอปพลิเคชันครับ - Client ใช้ JWT Access Token เรียก Resource Server: แอปพลิเคชันจะเก็บ Access Token (ที่เป็น JWT) และ Refresh Token ไว้ เมื่อแอปพลิเคชันต้องการเข้าถึงข้อมูลจาก Resource Server (เช่น Google Photos API) มันจะแนบ JWT Access Token ไปใน HTTP Header ของทุกๆ คำขอ ในรูปแบบ
Authorization: Bearer <your_jwt_access_token> - Resource Server ตรวจสอบ JWT: เมื่อ Resource Server ได้รับคำขอพร้อม JWT Access Token มันจะตรวจสอบลายเซ็นของ JWT โดยใช้ Public Key หรือ Secret Key ที่รู้กันกับ Authorization Server หากลายเซ็นถูกต้องและ Token ยังไม่หมดอายุ Resource Server จะอ่านข้อมูลสิทธิ์จาก Payload ของ JWT และอนุญาตให้เข้าถึงข้อมูลได้ตามสิทธิ์ที่ระบุ โดยไม่ต้องไปสอบถาม Authorization Server อีกครั้งครับ
- การใช้ Refresh Token: เมื่อ JWT Access Token หมดอายุ แอปพลิเคชันจะใช้ Refresh Token ที่เก็บไว้ ส่งกลับไปยัง Authorization Server เพื่อขอ JWT Access Token ใหม่ โดยที่ผู้ใช้งานไม่ต้อง Login ซ้ำครับ
จะเห็นได้ว่า OAuth 2.0 เป็นตัวจัดการกระบวนการ “การได้มา” ซึ่ง Access Token ส่วน JWT เป็น “รูปแบบ” ของ Access Token นั้นๆ ที่บรรจุข้อมูลสิทธิ์และรับประกันความถูกต้องด้วยลายเซ็นดิจิทัล ทำให้ระบบมีความปลอดภัย ยืดหยุ่น และปรับขนาดได้ดีเยี่ยมครับ
ตัวอย่าง Code Snippets และการนำไปใช้งานจริง
เพื่อให้เห็นภาพการใช้งาน JWT มากขึ้น เรามาดูตัวอย่างโค้ดง่ายๆ ด้วย Node.js และไลบรารี jsonwebtoken ซึ่งเป็นที่นิยมกันนะครับ
การสร้าง JWT ด้วย Node.js
สมมติว่าคุณมี Server-side application ที่ทำหน้าที่เป็น Authentication Server และต้องการสร้าง JWT ให้กับผู้ใช้งานที่ Login สำเร็จครับ
// ต้องติดตั้งไลบรารี: npm install jsonwebtoken
const jwt = require('jsonwebtoken');
// Secret Key ของคุณ ควรเก็บในตัวแปรสภาพแวดล้อม (environment variable)
// ไม่ควร hardcode แบบนี้ใน Production
const JWT_SECRET = 'your_super_secret_key_that_no_one_knows';
// ข้อมูล Payload ที่ต้องการใส่ใน JWT
const userPayload = {
userId: 'user123',
username: 'john.doe',
roles: ['user', 'editor']
};
// ตัวเลือกสำหรับการสร้าง JWT
const jwtOptions = {
expiresIn: '1h', // Token จะหมดอายุใน 1 ชั่วโมง
issuer: 'your-auth-server.com', // ผู้ออก Token
audience: 'your-resource-server.com' // ผู้รับ Token
};
try {
// สร้าง JWT
const token = jwt.sign(userPayload, JWT_SECRET, jwtOptions);
console.log('Generated JWT:', token);
// ตัวอย่าง: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ1c2VyMTIzIiwidXNlcm5hbWUiOiJqb2huLmRvZSIsInJvbGVzIjpbInVzZXIiLCJlZGl0b3IiXSwiaWF0IjoxNjcyNzgwODAwLCJleHAiOjE2NzI3ODQ0MDAsImlzcyI6InlvdXItYXV0aC1zZXJ2ZXIuY29tIiwiYXVkIjoiYWRtaW4tYXBwLmNvbSJ9.บางอย่างที่ยาวๆ
} catch (error) {
console.error('Error generating JWT:', error.message);
}
โค้ดด้านบนจะสร้าง JWT ขึ้นมาหนึ่งตัว ซึ่งมีข้อมูล userId, username, roles พร้อมกับ iat, exp, iss และ aud ตามที่คุณกำหนดครับ จากนั้นคุณก็จะส่ง JWT นี้กลับไปให้ Client ครับ
การตรวจสอบ JWT ด้วย Node.js
เมื่อ Client ส่ง JWT กลับมายัง Resource Server ของคุณ คุณจะต้องตรวจสอบความถูกต้องของ JWT นั้นก่อนที่จะอนุญาตให้เข้าถึงทรัพยากรครับ
const jwt = require('jsonwebtoken');
const JWT_SECRET = 'your_super_secret_key_that_no_one_knows'; // Secret Key เดียวกันกับที่ใช้สร้าง
// สมมติว่านี่คือ JWT ที่ Client ส่งมาใน Authorization header
const receivedJwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ1c2VyMTIzIiwidXNlcm5hbWUiOiJqb2huLmRvZSIsInJvbGVzIjpbInVzZXIiLCJlZGl0b3IiXSwiaWF0IjoxNjcyNzgwODAwLCJleHAiOjE2NzI3ODQ0MDAsImlzcyI6InlvdXItYXV0a-zZXJ2ZXIuY29tIiwiYXVkIjoiYWRtaW4tYXBwLmNvbSJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; // JWT ที่ถูกสร้างจากตัวอย่างก่อนหน้า
try {
// ตรวจสอบ JWT
const decoded = jwt.verify(receivedJwt, JWT_SECRET, {
issuer: 'your-auth-server.com',
audience: 'your-resource-server.com'
});
console.log('Decoded JWT Payload:', decoded);
// ผลลัพธ์:
// Decoded JWT Payload: {
// userId: 'user123',
// username: 'john.doe',
// roles: [ 'user', 'editor' ],
// iat: 1672780800,
// exp: 1672784400,
// iss: 'your-auth-server.com',
// aud: 'your-resource-server.com'
// }
// ตอนนี้คุณสามารถใช้ข้อมูลใน 'decoded' เพื่อกำหนดสิทธิ์การเข้าถึงได้
if (decoded.roles.includes('editor')) {
console.log('User is an editor, access granted to protected resource.');
} else {
console.log('User is not an editor, access denied.');
}
} catch (error) {
console.error('Error verifying JWT:', error.message);
// จัดการกับข้อผิดพลาด เช่น Token หมดอายุ, Token ไม่ถูกต้อง, ลายเซ็นไม่ตรง
if (error.name === 'TokenExpiredError') {
console.log('JWT is expired.');
} else if (error.name === 'JsonWebTokenError') {
console.log('Invalid JWT signature or format.');
}
}
ฟังก์ชัน jwt.verify() จะตรวจสอบทั้งลายเซ็นและวันหมดอายุ (exp) ของ Token ครับ หากมีสิ่งใดผิดปกติก็จะโยน Exception ออกมาทันที
การส่ง JWT จาก Client ไปยัง Resource Server
จากฝั่ง Client (เช่น เว็บเบราว์เซอร์, โมบายล์แอป) เมื่อได้รับ JWT Access Token มาแล้ว จะต้องแนบ Token นี้ไปในทุกๆ คำขอที่จะเข้าถึง Protected Resources ครับ โดยส่วนใหญ่จะแนบไปใน HTTP Header ชื่อ Authorization ในรูปแบบ “Bearer Token” ครับ
// สมมติว่าคุณเก็บ JWT Access Token ไว้ในตัวแปรนี้
const accessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjIsImlzQWRtaW4iOnRydWV9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
// ตัวอย่างการส่งคำขอด้วย JavaScript Fetch API (ใน Browser)
async function fetchProtectedData() {
try {
const response = await fetch('https://your-resource-server.com/api/protected-data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}` // แนบ JWT Access Token ตรงนี้
}
});
if (!response.ok) {
if (response.status === 401) {
console.error('Unauthorized: JWT is invalid or expired.');
// อาจจะต้อง redirect ไปหน้า login หรือใช้ refresh token เพื่อขอ access token ใหม่
} else {
console.error('Error fetching data:', response.statusText);
}
return;
}
const data = await response.json();
console.log('Protected Data:', data);
} catch (error) {
console.error('Network error or unexpected:', error);
}
}
fetchProtectedData();
นี่เป็นเพียงตัวอย่างพื้นฐานนะครับ ในการใช้งานจริงอาจจะต้องมีการจัดการกับข้อผิดพลาด การหมดอายุของ Token และการ Refresh Token ที่ซับซ้อนกว่านี้ครับ
ข้อดีและข้อเสียของการใช้ OAuth 2.0 และ JWT ร่วมกัน
การนำสองเทคโนโลยีนี้มารวมกันจะช่วยให้ระบบของคุณมีความสามารถในการอนุญาตและยืนยันตัวตนที่แข็งแกร่งขึ้น แต่ก็มาพร้อมกับความท้าทายบางประการครับ
ข้อดี
- ความปลอดภัยที่แข็งแกร่ง: OAuth 2.0 จัดการเรื่องการอนุญาตอย่างปลอดภัย ผู้ใช้งานไม่ต้องเปิดเผยข้อมูลรับรองตัวตน ส่วน JWT รับประกันความถูกต้องของข้อมูลและแหล่งที่มา
- Statelessness ของ Resource Server: เมื่อ Resource Server ได้รับ JWT ก็สามารถตรวจสอบสิทธิ์ได้ทันทีโดยไม่ต้องเรียกฐานข้อมูลหรือ Authentication Server เพิ่มเติม ทำให้ Resource Server มีความ Stateless และปรับขนาดได้ง่าย
- Scalability ที่ดีเยี่ยม: ระบบที่ใช้ JWT เป็น Access Token สามารถรองรับผู้ใช้งานจำนวนมากและการขยายตัวของ Microservices ได้ดี เนื่องจากลดภาระในการจัดการ Session บน Server
- ความยืดหยุ่น: สามารถใช้ JWT กับ API gateway, Load balancer และ Microservices ได้อย่างราบรื่น
- ประสบการณ์ผู้ใช้งานที่ดีขึ้น: ด้วย Refresh Token ผู้ใช้งานไม่จำเป็นต้อง Login ซ้ำบ่อยๆ แม้ Access Token จะมีอายุสั้นก็ตาม
- มาตรฐานอุตสาหกรรม: ทั้ง OAuth 2.0 และ JWT เป็นมาตรฐานที่ได้รับการยอมรับ ทำให้การพัฒนาและการทำงานร่วมกันกับบริการอื่นๆ เป็นไปได้ง่ายขึ้น
- ลด Latency: Resource Server ไม่ต้องทำการ Query ฐานข้อมูลเพื่อตรวจสอบสิทธิ์ทุกครั้งที่ได้รับคำขอ เพราะข้อมูลสิทธิ์อยู่ใน JWT แล้ว
ข้อเสีย
- ความซับซ้อนในการตั้งค่า: การทำความเข้าใจและตั้งค่า OAuth 2.0 (โดยเฉพาะ Grant Types ต่างๆ) และการจัดการ JWT อย่างถูกต้องอาจต้องใช้ความรู้และความเข้าใจที่ลึกซึ้ง
- ปัญหาการเพิกถอน Token (Revocation): การเพิกถอน JWT ก่อนหมดอายุเป็นเรื่องยาก เนื่องจากเป็น Stateless หาก Access Token ที่เป็น JWT ถูกขโมย ผู้โจมตีสามารถใช้งานได้จนกว่าจะหมดอายุ (เว้นแต่จะมีการใช้ Blacklist หรือ Revocation List ซึ่งเพิ่มความซับซ้อน)
- ขนาดของ Token: หากใส่ข้อมูล (Claims) จำนวนมากใน Payload ของ JWT มันอาจมีขนาดใหญ่ขึ้น ทำให้ HTTP Request Header มีขนาดใหญ่ขึ้น ซึ่งอาจส่งผลต่อประสิทธิภาพเล็กน้อย
- การจัดการ Secret Key: การปกป้อง Secret Key ที่ใช้ในการลงชื่อ JWT เป็นสิ่งสำคัญสูงสุด หาก Secret Key รั่วไหล ระบบจะถูกบุกรุกทั้งหมด
- ความเสี่ยงต่อ XSS และ CSRF: การจัดเก็บ JWT ใน Client (โดยเฉพาะใน Web Browser) ต้องทำอย่างระมัดระวังเพื่อป้องกันการโจมตีแบบ XSS (หากเก็บใน Local Storage) หรือ CSRF (หากเก็บใน Cookie โดยไม่มีมาตรการป้องกัน)
- ไม่เหมาะกับทุกกรณี: สำหรับแอปพลิเคชันขนาดเล็กหรือระบบที่ต้องการการควบคุม Session แบบเรียลไทม์อย่างเข้มงวด การใช้ Session-based authentication อาจจะเหมาะสมกว่าและง่ายกว่า
การพิจารณาข้อดีข้อเสียเหล่านี้อย่างรอบคอบจะช่วยให้คุณตัดสินใจได้ว่าการนำ OAuth 2.0 และ JWT มาใช้งานร่วมกันนั้นเหมาะสมกับความต้องการและข้อจำกัดของโครงการของคุณหรือไม่ครับ
คำถามที่พบบ่อย (FAQ)
Q1: OAuth 2.0 กับ OpenID Connect ต่างกันอย่างไร?
A1: OAuth 2.0 เป็นเฟรมเวิร์กสำหรับการ อนุญาต (Authorization) ครับ คือการให้สิทธิ์แอปพลิเคชันเข้าถึงทรัพยากรในนามของผู้ใช้งาน โดยไม่ได้เน้นการยืนยันตัวตนของผู้ใช้งานโดยตรง ส่วน OpenID Connect (OIDC) เป็นเลเยอร์ที่สร้างอยู่บน OAuth 2.0 อีกทีหนึ่ง เพื่อเพิ่มความสามารถในการ ยืนยันตัวตน (Authentication) ของผู้ใช้งานครับ OIDC จะออก ID Token (ซึ่งเป็น JWT ชนิดหนึ่ง) ที่มีข้อมูลระบุตัวตนของผู้ใช้งาน (เช่น ชื่อ, อีเมล) และยืนยันว่าผู้ใช้งานได้ Login เข้าสู่ระบบแล้ว นั่นคือ OIDC ใช้ OAuth 2.0 เป็นกลไกในการได้มาซึ่ง ID Token และ Access Token เพื่อยืนยันตัวตนและอนุญาตตามลำดับครับ
Q2: ควรเก็บ Access Token (ที่เป็น JWT) ไว้ที่ไหนใน Client?
A2: การจัดเก็บ Access Token ใน Client (โดยเฉพาะในเว็บเบราว์เซอร์) เป็นหัวข้อที่มีการถกเถียงกันมากครับ ไม่มีวิธีใดที่สมบูรณ์แบบ แต่ละวิธีมีข้อดีข้อเสีย:
- Local Storage/Session Storage: ง่ายต่อการเข้าถึงด้วย JavaScript แต่เสี่ยงต่อการโจมตีแบบ XSS (Cross-Site Scripting) หากมีช่องโหว่บนเว็บไซต์ ผู้โจมตีสามารถอ่าน Token ไปใช้งานได้
- HTTP-only Cookie: ป้องกันการอ่าน Token ด้วย JavaScript (ป้องกัน XSS) ได้ดีกว่า แต่ยังคงเสี่ยงต่อการโจมตีแบบ CSRF (Cross-Site Request Forgery) หากไม่มีมาตรการป้องกันเพิ่มเติม (เช่น SameSite attribute, CSRF token) และไม่สามารถเข้าถึงด้วย JavaScript ได้โดยตรง
- In-memory (ในหน่วยความจำของแอปพลิเคชัน): ปลอดภัยที่สุดในแแง่ของการไม่ตกค้างหลังปิดแอป/เบราว์เซอร์ แต่ Token จะหายไปเมื่อรีเฟรชหน้าหรือปิดแอป ต้องมีการเรียกขอ Token ใหม่บ่อยๆ
สำหรับ SPA ที่ใช้ JWT ควรพิจารณาใช้ HTTP-only, Secure, SameSite=Lax/Strict Cookie สำหรับ Refresh Token เพื่อขอ Access Token ใหม่ที่เก็บในหน่วยความจำ หรือ Local Storage/Session Storage (ถ้ามั่นใจว่าไม่มี XSS) และมีมาตรการป้องกัน CSRF ที่เหมาะสมครับ อ่านเพิ่มเติมเกี่ยวกับการจัดเก็บ Token
Q3: จะ Revoke JWT ได้อย่างไร?
A3: เนื่องจาก JWT เป็น Stateless การเพิกถอนก่อนหมดอายุจึงเป็นความท้าทายครับ แนวทางแก้ไขทั่วไปได้แก่:
- Blacklisting/Revocation List: เก็บรายการของ Token ID (
jticlaim) ที่ถูกเพิกถอนไว้ในฐานข้อมูลหรือ Cache (เช่น Redis) และทุกครั้งที่ Resource Server ได้รับ JWT ต้องตรวจสอบว่า Token นั้นอยู่ใน Blacklist หรือไม่ วิธีนี้เพิ่มภาระให้กับ Resource Server เล็กน้อย - Short Expiration Time: กำหนดให้ Access Token มีอายุสั้นมากๆ (เช่น 5-15 นาที) เพื่อลดช่วงเวลาที่ Token ที่ถูกขโมยสามารถใช้งานได้ และใช้ Refresh Token เพื่อขอ Access Token ใหม่
- Changing Secret Key: หากสงสัยว่า Secret Key รั่วไหล การเปลี่ยน Secret Key จะทำให้ JWT ทั้งหมดที่ถูกสร้างด้วย Secret Key เก่าไม่สามารถตรวจสอบได้อีกต่อไป (แต่จะส่งผลกระทบต่อผู้ใช้งานทุกคน)
ไม่มีวิธีใดสมบูรณ์แบบ แต่การผสมผสาน Short Expiration Time กับ Blacklisting สำหรับสถานการณ์ที่จำเป็น เช่น ผู้ใช้งาน Logout หรือเปลี่ยนรหัสผ่าน มักจะเป็นแนวทางที่สมดุลครับ
Q4: Refresh Token คืออะไร และปลอดภัยแค่ไหน?
A4: Refresh Token คือ Token ที่มีอายุยาวนานกว่า Access Token ครับ มีไว้สำหรับ Client ใช้ในการขอ Access Token ใหม่จาก Authorization Server โดยไม่ต้องให้ผู้ใช้งานต้อง Login ซ้ำอีกครั้งเมื่อ Access Token เดิมหมดอายุครับ
Refresh Token มีความปลอดภัยน้อยกว่า Access Token ในแง่ที่ว่ามันมีอายุยืนยาวกว่า ดังนั้นหากถูกขโมย ผู้โจมตีอาจนำไปใช้ขอ Access Token ใหม่ได้เรื่อยๆ ครับ เพื่อเพิ่มความปลอดภัย ควร:
- เก็บ Refresh Token ใน HTTP-only, Secure Cookie (สำหรับ Web) หรือ Secure Storage (สำหรับ Mobile)
- กำหนดอายุของ Refresh Token ให้เหมาะสม (อาจจะหลายวัน, หลายสัปดาห์)
- ผูก Refresh Token กับ Client ID และ Resource Owner เพื่อให้ใช้ได้เฉพาะ Client ที่ถูกต้องและผู้ใช้งานที่ถูกต้อง
- มีกลไกในการเพิกถอน Refresh Token ได้ทันทีที่พบความผิดปกติ
Q5: JWT มีความปลอดภัยจากการถูกดักฟัง (Man-in-the-Middle) หรือไม่?
A5: JWT ด้วยตัวมันเองไม่ได้ป้องกันการดักฟัง (Eavesdropping) ครับ ข้อมูลใน Payload ของ JWT ไม่ได้ถูกเข้ารหัส (Encrypted) แต่เป็นเพียงการเข้ารหัสแบบ Base64Url-encoded ซึ่งสามารถถอดรหัสกลับมาอ่านได้ง่าย ดังนั้นข้อมูลที่อยู่ใน JWT จะถูกเปิดเผยหากถูกดักจับระหว่างทาง
เพื่อป้องกันการดักฟัง คุณ ต้องใช้งาน JWT ร่วมกับ HTTPS (SSL/TLS) เสมอ ครับ HTTPS จะเข้ารหัสการสื่อสารทั้งหมดระหว่าง Client และ Server ทำให้ผู้โจมตีไม่สามารถดักฟังและอ่านข้อมูล JWT ได้ หากคุณต้องการเข้ารหัสข้อมูลใน JWT จริงๆ จะต้องใช้ JWE (JSON Web Encryption) แทน JWS ครับ
สรุปและ Call-to-Action
ตลอดบทความนี้ เราได้สำรวจเจาะลึกถึง OAuth 2.0 และ JWT Authentication ซึ่งเป็นสองเสาหลักของระบบความปลอดภัยในโลกของเว็บแอปพลิเคชันและ API ในปัจจุบันครับ
- OAuth 2.0 มอบกรอบการทำงานที่แข็งแกร่งสำหรับการ อนุญาต ให้แอปพลิเคชันเข้าถึงทรัพยากรในนามของผู้ใช้งาน โดยไม่ต้องให้ข้อมูลรับรองตัวตนโดยตรง ซึ่งช่วยลดความเสี่ยงด้านความปลอดภัยได้อย่างมาก
- JWT เป็นมาตรฐานสำหรับ การส่งข้อมูลที่น่าเชื่อถือ ซึ่งมักจะถูกนำมาใช้เป็น Access Token ภายในกรอบการทำงานของ OAuth 2.0 ทำให้ Resource Server สามารถตรวจสอบสิทธิ์ได้อย่างรวดเร็วและมีประสิทธิภาพโดยไม่ต้องพึ่งพาเซิร์ฟเวอร์กลางทุกครั้ง
เมื่อนำทั้งสองเทคโนโลยีนี้มาผสานรวมกันอย่างถูกต้อง คุณจะได้รับระบบยืนยันตัวตนและการอนุญาตที่มีความปลอดภัยสูง ปรับขนาดได้ดีเยี่ยม (Scalable) และมอบประสบการณ์การใช้งานที่ราบรื่นยิ่งขึ้น แต่ก็ต้องแลกมาด้วยความซับซ้อนในการทำความเข้าใจและการนำไปใช้งานที่ต้องระมัดระวังเป็นพิเศษในเรื่องของการจัดการ Secret Key, การหมดอายุของ Token และการป้องกันการโจมตีต่างๆ ครับ
การเข้าใจและนำหลักการเหล่านี้ไปใช้ได้อย่างถูกต้องคือสิ่งสำคัญอย่างยิ่งในการสร้างแอปพลิเคชันที่ปลอดภัยและน่าเชื่อถือในยุคดิจิทัลครับ หากคุณกำลังวางแผนที่จะพัฒนาระบบที่ซับซ้อนและต้องการความเชี่ยวชาญด้านความปลอดภัยในการยืนยันตัวตนและการอนุญาต หรือต้องการคำปรึกษาในการประยุกต์ใช้ OAuth 2.0 และ JWT ในโครงการของคุณ ทีมงานผู้เชี่ยวชาญของ SiamLancard.com พร้อมที่จะให้คำปรึกษาและสนับสนุนคุณในทุกขั้นตอนครับ อย่าลังเลที่จะติดต่อเราเพื่อพูดคุยถึงความต้องการของคุณ เรายินดีเป็นส่วนหนึ่งในการสร้างสรรค์ระบบที่แข็งแกร่งและปลอดภัยให้กับธุรกิจของคุณครับ