Authorization
“คุณมีสิทธิ์ทำสิ่งที่กำลังจะทำหรือไม่?”
คืออะไร
หัวข้อที่มีชื่อว่า “คืออะไร”“Do you have the permission to do what you are trying to do?” นี่คือแนวคิดหลักของ authorization — การตรวจสอบว่า entity หนึ่งมีสิทธิ์ทำการกระทำนั้นหรือไม่
Authorization ใช้ตรวจว่า entity มีสิทธิ์เข้าถึงเพื่อทำการกระทำหนึ่งหรือไม่ เช่น การเข้าถึง resource การอัปเดต record หรือการลบข้อมูล ที่สำคัญคือ authorization ไม่จำเป็นต้องมี authentication เสมอไป ระบบอาจอนุญาตให้เข้าถึงแบบ anonymous ได้ (การตรวจสิทธิ์แบบ anonymous เกิดขึ้นเมื่อไม่มีผู้ใช้ที่ผ่านการยืนยันตัวตนแล้วพยายามทำการกระทำ) แต่โดยทั่วไป resource และการกระทำมักถูกจำกัดเฉพาะผู้ใช้ที่ผ่าน authentication แล้ว — authorization จึงมักตามหลัง authentication
Microsoft สรุปความต่างไว้กระชับมาก: authentication คือการพิสูจน์ว่า “คุณเป็นใคร” (who are you) ส่วน authorization คือการให้สิทธิ์ทำสิ่งใดสิ่งหนึ่งกับผู้ที่ผ่านการยืนยันตัวตนแล้ว — “what are you allowed to do” ใน stack ของ OAuth 2.0 / OpenID Connect protocol ทั้งสองแยกหน้าที่กันชัดเจน: OpenID Connect ทำ authentication ส่วน OAuth 2.0 ทำ authorization (ออก access token ที่บอกว่า client หรือ user ทำอะไรได้บ้างกับ resource ใด)
Wikipedia นิยาม authorization ว่าเป็น function ของการกำหนดว่า subject หนึ่ง (ผู้ใช้ ซอฟต์แวร์ หรือฮาร์ดแวร์) มีสิทธิ์เข้าถึง resource ที่ระบุหรือไม่ โดย authorization เป็นฝั่ง “กำหนดนโยบาย” (policy) ส่วน access control enforcement เป็นฝั่ง “บังคับใช้” นโยบายนั้นจริง ๆ ตอนมี request เข้ามา — สองคำนี้มักถูกใช้แทนกันแต่จริง ๆ แยกความรับผิดชอบกันได้
อย่าสับสน 401 กับ 403: ในการเข้าถึง API รหัสสถานะฝั่ง client (400–499) มีสองตัวที่เกี่ยวกับสิทธิ์ — 401 Unauthorized แม้ชื่อจะว่า “unauthorized” แต่แท้จริงหมายถึง ยังไม่ผ่านการยืนยันตัวตน (unauthenticated) ส่วน 403 Forbidden หมายถึงผ่านการยืนยันตัวตนแล้ว แต่ ถูกปฏิเสธสิทธิ์ ในการกระทำนั้น
model ของ authorization ที่พบบ่อย
หัวข้อที่มีชื่อว่า “model ของ authorization ที่พบบ่อย”- RBAC (Role-Based Access Control) — model ที่ใช้กันแพร่หลายที่สุด นักพัฒนากำหนด role เพื่ออธิบายกิจกรรมที่ entity หนึ่งทำได้ แล้วให้สิทธิ์กับ role แทนที่จะให้กับผู้ใช้เป็นราย ๆ ผู้ดูแลระบบก็แค่ assign role ให้ entity ต่าง ๆ
- ABAC (Attribute-Based Access Control) — ละเอียดกว่า RBAC โดยพิจารณา attribute ของ subject, resource และ environment ร่วมกัน เช่น อนุญาตให้ผู้ใช้ที่มี attribute “เป็นผู้จัดการ” เข้าถึง file ที่ tag ว่า “managers only” เฉพาะช่วงเวลา 9:00–17:00 ของวันทำงาน
- ACL (Access Control List) — นโยบายที่เขียนเป็นรายการสิทธิ์ต่อ resource โดยตรง เหมาะกับกรณีสิทธิ์ผูกกับ object แต่ละชิ้นมากกว่าผูกกับ role
- Policy-based authorization — รวม requirement หลายตัวเข้าเป็น policy ที่ตั้งชื่อได้ แล้วประเมินผ่าน handler ทำให้ตรรกะ authorization อยู่รวมศูนย์และทดสอบได้ง่ายกว่าการฝัง
if (user.Role == "Admin")กระจายทั่ว code
ทำอย่างไร
หัวข้อที่มีชื่อว่า “ทำอย่างไร”- Deny by default — เริ่มจากปฏิเสธการเข้าถึงทุกอย่าง แล้วเปิดสิทธิ์เฉพาะที่จำเป็นเท่านั้น (OWASP เรียกหลักการนี้ว่าการหลีกเลี่ยง “violation of least privilege”) resource สาธารณะเท่านั้นที่ควรเปิดให้เข้าถึงได้โดยไม่ต้องตรวจ
- บังคับใช้ authorization ที่ trusted server-side เท่านั้น อย่าพึ่งการซ่อนปุ่มหรือ route ฝั่ง client เพราะ attacker แก้ HTML/JS หรือยิง API ตรงได้เสมอ ทุก endpoint ที่แก้ไขหรืออ่านข้อมูลอ่อนไหวต้องตรวจสิทธิ์ฝั่ง server ซ้ำ ไม่ว่าฝั่ง UI จะซ่อนอะไรไว้ก็ตาม
- เลือก model ให้เหมาะกับความซับซ้อนของกฎ เริ่มจาก role-based ถ้ากฎเรียบง่าย (เช่น Admin/Editor/Viewer) แล้วขยับไป claims-based หรือ policy-based เมื่อกฎซับซ้อนขึ้น (เช่น ต้องพิจารณาความเป็นเจ้าของ resource เวลา หรือ context อื่น) ส่วน ABAC เหมาะกับกรณีที่กฎอิงหลาย attribute พร้อมกันและเปลี่ยนบ่อย
- รวมจุดตรวจสอบให้เหลือน้อยที่สุด ใช้แนวทางแบบ Single Point of Enforcement เพื่อไม่ให้ logic การตรวจสิทธิ์กระจัดกระจายไปทั่ว codebase ซึ่งเสี่ยงต่อการลืมตรวจในบาง endpoint
- ป้องกัน Insecure Direct Object Reference (IDOR) อย่าเชื่อ id ที่ผู้ใช้ส่งมาว่าเป็นของเขาจริง ต้องตรวจความเป็นเจ้าของ/สิทธิ์ต่อ resource นั้นทุกครั้งที่เข้าถึงด้วย id
- แยก policy ออกจาก code business logic เพื่อให้ทดสอบ ตรวจสอบ (audit) และปรับเปลี่ยนกฎได้โดยไม่ต้องแตะ logic หลัก — ตรงนี้ policy-based authorization ใน framework สมัยใหม่ (เช่น ASP.NET Core) ช่วยได้มาก
- Log และ monitor การถูกปฏิเสธสิทธิ์ เพื่อช่วยตรวจจับการโจมตีหรือ bug ด้าน authorization ตั้งแต่เนิ่น ๆ
flowchart TD
A[Request เข้ามา] --> B{ผ่าน Authentication แล้วหรือไม่}
B -- ไม่ผ่าน --> C[401 Unauthorized]
B -- ผ่านหรือ Anonymous ที่อนุญาต --> D{ตรวจ Authorization Policy}
D -- ไม่มีสิทธิ์ --> E[403 Forbidden]
D -- มีสิทธิ์ --> F[ดำเนินการตามคำขอ]
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”ตัวอย่างต่อไปนี้แสดง authorization สามระดับใน ASP.NET Core ตั้งแต่แบบง่ายที่สุด (role-based) ไปจนถึงแบบยืดหยุ่น (policy-based ที่ตรวจความเป็นเจ้าของ resource ด้วย)
// 1) Role-based — ง่ายที่สุด เหมาะกับกฎแบบ Admin/Editor/Viewer[Authorize(Roles = "Admin,Editor")]public IActionResult DeleteArticle(int id){ // เข้าถึงได้เฉพาะผู้ใช้ที่มี role Admin หรือ Editor return Ok();}
// 2) Claims-based — ตรวจ claim เฉพาะ เช่น อายุ หรือระดับสมาชิกpublic class MinimumAgeRequirement : IAuthorizationRequirement{ public int MinimumAge { get; } public MinimumAgeRequirement(int minimumAge) => MinimumAge = minimumAge;}
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>{ protected override Task HandleRequirementAsync( AuthorizationHandlerContext context, MinimumAgeRequirement requirement) { var birthDateClaim = context.User.FindFirst(c => c.Type == "birthdate"); if (birthDateClaim is null) { return Task.CompletedTask; // ไม่มี claim นี้ = ไม่ผ่าน ปล่อยให้ล้มเหลว (deny by default) }
var age = CalculateAge(DateTime.Parse(birthDateClaim.Value)); if (age >= requirement.MinimumAge) { context.Succeed(requirement); }
return Task.CompletedTask; }
private static int CalculateAge(DateTime birthDate) { var today = DateTime.Today; var age = today.Year - birthDate.Year; if (birthDate.Date > today.AddYears(-age)) age--; // ยังไม่ถึงวันเกิดปีนี้ return age; }}
// 3) Policy-based ที่ตรวจความเป็นเจ้าของ resource — ป้องกัน IDORpublic class SameOwnerRequirement : IAuthorizationRequirement { }
public class SameOwnerHandler : AuthorizationHandler<SameOwnerRequirement, Order>{ protected override Task HandleRequirementAsync( AuthorizationHandlerContext context, SameOwnerRequirement requirement, Order resource) { var userId = context.User.FindFirst("sub")?.Value;
// ตรวจว่า order นี้เป็นของผู้ใช้ที่ยิง request จริงหรือไม่ // แทนที่จะเชื่อ orderId ที่ client ส่งมาตรง ๆ if (userId == resource.OwnerId) { context.Succeed(requirement); }
return Task.CompletedTask; }}
// การลงทะเบียน handler และ policy ใน Program.csbuilder.Services.AddScoped<IAuthorizationHandler, MinimumAgeHandler>();builder.Services.AddScoped<IAuthorizationHandler, SameOwnerHandler>();builder.Services.AddAuthorization(options =>{ options.AddPolicy("MinimumAge18", policy => policy.Requirements.Add(new MinimumAgeRequirement(18)));});ในตัวอย่างที่ 3 ผู้เรียก endpoint ต้องเป็นเจ้าของ Order จริงเท่านั้นจึงจะผ่าน แม้ว่าจะยิง orderId ของคนอื่นเข้ามาก็ตาม — นี่คือรูปแบบการป้องกัน Insecure Direct Object Reference (IDOR) ที่ OWASP เตือนไว้บ่อยครั้ง
ประโยชน์และข้อควรระวัง
หัวข้อที่มีชื่อว่า “ประโยชน์และข้อควรระวัง”ประโยชน์
- แยกความรับผิดชอบชัดเจนระหว่าง “คุณเป็นใคร” (authentication) กับ “คุณทำอะไรได้บ้าง” (authorization) ทำให้ระบบยืดหยุ่นขึ้น เช่น รองรับ anonymous access สำหรับบาง resource ได้โดยไม่ต้องมี authentication เลย
- model อย่าง RBAC/ABAC/policy-based ทำให้กฎการเข้าถึงอยู่รวมศูนย์ ตรวจสอบและปรับเปลี่ยนได้ง่ายกว่าการฝัง
ifกระจายในทุกที่ - ลดความเสี่ยงจากช่องโหว่ Broken Access Control ซึ่ง OWASP จัดเป็นความเสี่ยงอันดับต้น ๆ ของเว็บ application มาต่อเนื่องหลายปี
ข้อควรระวัง
- อย่าสับสน 401 กับ 403 — การส่งรหัสผิดทำให้ client (หรือทีม debug) เข้าใจสาเหตุของปัญหาผิด
- authorization ที่ตรวจแค่ฝั่ง client (เช่น ซ่อนปุ่มด้วย JavaScript) ไม่ใช่ authorization จริง ต้อง enforce ที่ server เสมอ
- RBAC ที่กฎซับซ้อนขึ้นเรื่อย ๆ (role explosion) อาจกลายเป็นภาระดูแล ลองพิจารณา ABAC หรือ policy-based เมื่อกฎเริ่มมีเงื่อนไขไขว้กันมาก
- การลืมตรวจ ownership ของ resource (ตรวจแค่ role แต่ไม่ตรวจว่าของใคร) คือสาเหตุอันดับต้นของ IDOR — ต้องตรวจทั้งสองมิติเสมอเมื่อ resource ผูกกับเจ้าของ
ที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “ที่เกี่ยวข้อง”- Authentication — ขั้นตอนพิสูจน์ตัวตนที่มักเกิดก่อน authorization
- Single Point of Enforcement — รวมจุดตรวจกฎ (รวมถึง authorization) ให้เหลือประตูเดียว
- Proxy — pattern ที่มักใช้แทรก access control ก่อนเรียก object จริง
- Guard Clause — เทคนิคตรวจเงื่อนไข (รวมถึงเงื่อนไขสิทธิ์) แล้วออกทันทีตั้งแต่ต้น method
แหล่งอ้างอิง
หัวข้อที่มีชื่อว่า “แหล่งอ้างอิง”- ที่มา · deviq.com/practices/authorization
- Authentication vs. authorization — Microsoft identity platform (Microsoft Learn)
- Authorization basics — Microsoft identity platform (Microsoft Learn)
- Authorization — Wikipedia
- Policy-based authorization in ASP.NET Core — Microsoft Learn
- A01 Broken Access Control — OWASP Top 10