ข้าม​ไป​ยัง​เนื้อหา

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 หมาย​ถึง​ผ่าน​การ​ยืนยัน​ตัวตน​แล้ว แต่ ถูก​ปฏิเสธ​สิทธิ์ ใน​การกระทำ​นั้น

  • 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
  1. Deny by default — เริ่ม​จาก​ปฏิเสธ​การ​เข้าถึง​ทุก​อย่าง แล้ว​เปิด​สิทธิ์​เฉพาะ​ที่​จำเป็น​เท่านั้น (OWASP เรียก​หลักการ​นี้​ว่าการ​หลีก​เลี่ยง “violation of least privilege”) resource สาธารณะ​เท่านั้น​ที่​ควร​เปิด​ให้​เข้าถึง​ได้​โดย​ไม่​ต้อง​ตรวจ
  2. บังคับ​ใช้ authorization ที่ trusted server-side เท่านั้น อย่า​พึ่ง​การ​ซ่อน​ปุ่ม​หรือ route ฝั่ง client เพราะ attacker แก้ HTML/JS หรือ​ยิง API ตรง​ได้​เสมอ ทุก endpoint ที่​แก้ไข​หรือ​อ่าน​ข้อมูล​อ่อนไหว​ต้อง​ตรวจ​สิทธิ์​ฝั่ง server ซ้ำ ไม่​ว่า​ฝั่ง UI จะ​ซ่อน​อะไร​ไว้​ก็ตาม
  3. เลือก model ให้​เหมาะ​กับ​ความ​ซับซ้อน​ของ​กฎ เริ่ม​จาก role-based ถ้า​กฎ​เรียบ​ง่าย (เช่น Admin/Editor/Viewer) แล้ว​ขยับ​ไป claims-based หรือ policy-based เมื่อ​กฎ​ซับซ้อน​ขึ้น (เช่น ต้อง​พิจารณา​ความ​เป็น​เจ้าของ resource เวลา หรือ context อื่น) ส่วน ABAC เหมาะ​กับ​กรณี​ที่​กฎ​อิง​หลาย attribute พร้อม​กัน​และ​เปลี่ยน​บ่อย
  4. รวม​จุด​ตรวจสอบ​ให้​เหลือ​น้อย​ที่สุด ใช้​แนวทาง​แบบ Single Point of Enforcement เพื่อ​ไม่​ให้ logic การ​ตรวจ​สิทธิ์​กระจัดกระจาย​ไป​ทั่ว codebase ซึ่ง​เสี่ยง​ต่อ​การ​ลืม​ตรวจ​ใน​บาง endpoint
  5. ป้องกัน Insecure Direct Object Reference (IDOR) อย่า​เชื่อ id ที่​ผู้​ใช้​ส่ง​มา​ว่า​เป็น​ของ​เขา​จริง ต้อง​ตรวจ​ความ​เป็น​เจ้าของ/สิทธิ์​ต่อ resource นั้น​ทุก​ครั้ง​ที่​เข้าถึง​ด้วย id
  6. แยก policy ออก​จาก code business logic เพื่อ​ให้​ทดสอบ ตรวจสอบ (audit) และ​ปรับ​เปลี่ยน​กฎ​ได้​โดย​ไม่​ต้อง​แตะ logic หลัก — ตรง​นี้ policy-based authorization ใน framework สมัย​ใหม่ (เช่น ASP.NET Core) ช่วย​ได้​มาก
  7. 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 — ป้องกัน IDOR
public 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.cs
builder.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