ข้ามไปยังเนื้อหา
Tayakorn
System Design Handbook

System Design Handbook

Authentication & Authorization — ใครเป็นใคร ทำอะไรได้

Sessions · JWT · OAuth 2.0 · OpenID Connect · RBAC · ABAC · ▶ ในคลิป 1:23:41 – 1:55:51

สองคำที่หน้าตาคล้ายแต่คนละเรื่อง: Authentication (AuthN) = พิสูจน์ว่า "คุณคือใคร" ส่วน Authorization (AuthZ) = ตัดสินว่า "คุณทำสิ่งนี้ได้ไหม" — เปรียบกับสนามบิน: ด่านตรวจพาสปอร์ตคือ authentication ส่วนตั๋ว boarding pass ที่บอกว่าคุณขึ้นเครื่องลำไหน ที่นั่ง class อะไร คือ authorization · ระบบตอบ 401 เมื่อ AuthN ล้มเหลว และ 403 เมื่อ AuthZ ล้มเหลว

7.1 ปัญหาตั้งต้น: HTTP จำใครไม่ได้

HTTP เป็น stateless — login สำเร็จแล้ว request ถัดไปเซิร์ฟเวอร์ก็ไม่รู้ว่าเป็นคนเดิม เราจึงต้องมี "หลักฐาน" แนบไปกับทุก request มีสองสำนักใหญ่:

7.2 สำนักที่หนึ่ง: Session + Cookie (Stateful)

หลัง login เซิร์ฟเวอร์สร้าง session record เก็บฝั่งตัวเอง (ใน Redis/DB) แล้วส่งแค่ session ID ใส่ cookie ให้เบราว์เซอร์ — ทุก request ถัดไป เบราว์เซอร์แนบ cookie อัตโนมัติ เซิร์ฟเวอร์เอา ID ไปเปิดดูว่าเป็นใคร

  • ข้อดี — เพิกถอนได้ทันที (ลบ session ทิ้ง = logout ทุกอุปกรณ์), ข้อมูล session อัปเดตสดเสมอ
  • ข้อเสีย — ทุก request ต้องเปิด lookup หนึ่งครั้ง และใน scale-out ต้องมี shared session store ให้ทุกเครื่องอ่านร่วมกัน (บทที่ 3)

7.3 สำนักที่สอง: JWT (Stateless Token)

JWT (JSON Web Token) พลิกแนวคิด: แทนที่จะเก็บข้อมูลฝั่งเซิร์ฟเวอร์ ก็ ยัดข้อมูลลงใน token แล้วเซ็นลายเซ็นดิจิทัลกำกับ เซิร์ฟเวอร์แค่ตรวจลายเซ็นก็เชื่อเนื้อหาข้างในได้เลย — ไม่ต้องเปิด DB

eyJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiI0MiIsInJvbGUiOiJhZG1pbiJ9 . SflKxwRJSMeKKF2QT4...HEADER{ "alg": "HS256", "typ": "JWT" }บอกอัลกอริทึมที่ใช้เซ็นPAYLOAD (Claims){ "sub": "42", "role": "admin", "exp": 1740000000 }ตัวตน สิทธิ์ และวันหมดอายุSIGNATUREHMAC(header + payload, secret)กันการปลอมแปลงเนื้อหา⚠ เข้ารหัสแบบ Base64 = ใครก็ “อ่าน” payload ได้ — ลายเซ็นกันแค่การ “แก้ไข” ไม่ใช่การอ่านห้ามใส่ข้อมูลลับ (รหัสผ่าน, เลขบัตร) ลงใน JWT เด็ดขาด
FIG 7.1 โครงสร้าง JWT สามส่วนคั่นด้วยจุด: header.payload.signature — แก้ payload แม้ตัวอักษรเดียว ลายเซ็นจะไม่ตรงทันที

จุดแลกเปลี่ยนที่ต้องท่องให้ขึ้นใจ: JWT เพิกถอนกลางอากาศไม่ได้ — token ที่เซ็นแล้วจะใช้ได้จนกว่าจะหมดอายุ (exp) ต่อให้ผู้ใช้ถูกแบนไปแล้วก็ตาม ทางแก้มาตรฐานคือคู่ token:

  • Access Token — อายุสั้นมาก (5–15 นาที) ใช้แนบทุก request ใน header Authorization: Bearer <token>
  • Refresh Token — อายุยาว (วัน/สัปดาห์) เก็บอย่างปลอดภัย ใช้ ขอ access token ใหม่ เมื่อตัวเก่าหมดอายุ — และฝั่งเซิร์ฟเวอร์เพิกถอน refresh token ได้ (จุดนี้ stateful) ทำให้แบนผู้ใช้มีผลภายในไม่กี่นาที
มิติSession + CookieJWT
เก็บ state ที่เซิร์ฟเวอร์ (Redis/DB)ตัว token เอง (client ถือ)
ตรวจสอบlookup ทุก requestตรวจลายเซ็น ไม่แตะ DB
เพิกถอนทันทีต้องรอหมดอายุ / ใช้คู่ refresh token
Scale-outต้องมี shared storeเครื่องไหนก็ตรวจได้เอง — เหมาะ microservices
เหมาะกับเว็บแอปดั้งเดิม, ระบบที่ความปลอดภัยนำAPI, mobile, ระบบกระจายหลาย service

7.4 OAuth 2.0 — มอบสิทธิ์โดยไม่มอบรหัสผ่าน

โจทย์ของ OAuth: แอป A อยากเข้าถึงข้อมูลของคุณในบริการ B (เช่น แอปตัดต่อรูปขอเข้าถึง Google Photos) — วิธีโบราณคือเอารหัสผ่าน Google ไปกรอกในแอป ซึ่งอันตรายสุดขีด OAuth 2.0 แก้ด้วยการให้ B ออก token ที่จำกัดขอบเขต (scope) และเพิกถอนได้ ให้ A แทน — ผู้เล่นมี 4 ฝ่าย:

  • Resource Owner — ผู้ใช้ (เจ้าของข้อมูล)
  • Client — แอปที่ขอสิทธิ์ (แอปตัดต่อรูป)
  • Authorization Server — ผู้ออก token (หน้า login ของ Google)
  • Resource Server — API ที่ถือข้อมูล (Google Photos API)
ผู้ใช้Client AppAuth ServerResource API1. กด “เชื่อมต่อ Google”2. redirect ไปหน้า login + ขอ scope3. ผู้ใช้ login + กดยินยอม (consent) ที่ Google โดยตรง4. กลับมาพร้อม authorization code5. แลก code + client secret6. ได้ access token (จำกัด scope)7. เรียก API ด้วย Bearer tokenรหัสผ่านผู้ใช้ไม่เคยผ่านมือ Client App เลยแม้แต่ครั้งเดียว
FIG 7.2 OAuth 2.0 Authorization Code Flow: ผู้ใช้กรอกรหัสที่ Auth Server เท่านั้น แอปได้แค่ token ขอบเขตจำกัดที่เพิกถอนได้

สองรายละเอียดที่ใช้จริง: scope จำกัดว่า token ทำอะไรได้ (photos.read อ่านได้แต่ลบไม่ได้) และ PKCE — ส่วนขยายภาคบังคับสำหรับ mobile/SPA ที่เก็บ client secret ไม่ได้ ใช้คู่ challenge/verifier กันคนดัก authorization code ไปแลก token แทนเรา

7.5 OpenID Connect (OIDC) — ชั้นตัวตนบน OAuth

OAuth 2.0 ออกแบบมาเพื่อ "มอบสิทธิ์เข้าถึง" ไม่ใช่ "ยืนยันตัวตน" — ปุ่ม "Sign in with Google" ที่เราเห็นทุกวันคือ OIDC: ชั้นบาง ๆ บน OAuth ที่เพิ่ม ID Token (เป็น JWT) ซึ่งบรรจุข้อมูลตัวตนที่ยืนยันแล้ว (ชื่อ, อีเมล, รูป) — จำง่าย ๆ: OAuth = สิทธิ์ (authorization), OIDC = ตัวตน (authentication) และแอปจำนวนมากใช้ทั้งคู่ใน flow เดียวกัน

7.6 Access Control — RBAC vs ABAC

รู้ตัวตนแล้ว ขั้นต่อไปคือตัดสินใจว่า "ทำได้ไหม" สองโมเดลหลัก:

RBAC (Role-Based)ABAC (Attribute-Based)
หลักการผูกสิทธิ์กับ "บทบาท" แล้วแจกบทบาทให้คนเขียนกฎจาก "คุณลักษณะ" ของผู้ใช้ + ทรัพยากร + บริบท
ตัวอย่างกฎrole = editor → แก้บทความได้ทุกชิ้นแก้ได้ถ้า user.dept == doc.dept และ time ∈ เวลางาน และ doc.status != locked
ความละเอียดหยาบ — สิทธิ์เหมาเข่งตาม roleละเอียดระดับเงื่อนไขรายชิ้น
ดูแลรักษาง่าย เข้าใจไว ตรวจสอบ (audit) ง่ายกฎซับซ้อน debug ยากกว่า — แต่ยืดหยุ่นสุด
เหมาะกับองค์กรโครงสร้างชัด (admin/editor/viewer)ระบบ multi-tenant, ข้อกำหนดกำกับดูแลซับซ้อน

ของจริงมักเป็น ลูกผสม: ใช้ RBAC เป็นโครงหลัก (เข้าใจง่าย) แล้วเสริมเงื่อนไข attribute เฉพาะจุดที่ต้องละเอียด เช่น "editor แก้บทความได้ เฉพาะของทีมตัวเอง"

สรุปบทที่ 7 — AuthN = คุณคือใคร (401) · AuthZ = ทำได้ไหม (403) · Session = เพิกถอนง่าย แต่ต้อง lookup · JWT = ตรวจเองได้ไม่แตะ DB แต่เพิกถอนยาก → ใช้ access สั้น + refresh ยาว · OAuth = มอบสิทธิ์ข้ามแอปโดยไม่มอบรหัสผ่าน · OIDC = ชั้นยืนยันตัวตนบน OAuth · RBAC เริ่มง่าย เสริม ABAC เมื่อต้องละเอียด

อ่านแบบเต็มเล่ม