วิธีเพิ่มการควบคุมการเข้าถึงตามบทบาท (RBAC) ในแอปของคุณ
ในการเพิ่มการควบคุมการเข้าถึงตามบทบาท ให้กำหนดบทบาทอย่างน้อยหนึ่งบทบาทให้ผู้ใช้แต่ละคน (เช่น ผู้ดูแลระบบ ผู้แก้ไข ผู้ดู) กำหนดว่าแต่ละบทบาททำการใดได้บ้าง และตรวจสิทธิ์เหล่านั้นบนเซิร์ฟเวอร์ ในทุกคำขอ ไม่ใช่แค่ซ่อนปุ่ม และตรวจด้วยว่าระเบียนที่กำลังเข้าถึงเป็นของบัญชีหรือทีมของผู้ใช้ เริ่มจากไม่กี่บทบาทที่กำหนดตายตัวในฐานข้อมูลของคุณ แล้วทดสอบโดยลองเข้าถึงข้อมูลของผู้ใช้คนอื่นโดยตรง
การยืนยันตัวตนกับการให้สิทธิ์
สองคำที่มักถูกสับสน:
- การยืนยันตัวตน (Authentication) คือ คุณคือใคร? ได้แก่การล็อกอิน ดู วิธีเพิ่มการล็อกอินในแอปของคุณ
- การให้สิทธิ์ (Authorisation) คือ คุณได้รับอนุญาตให้ทำอะไร? ได้แก่หน้า ระเบียน และการกระทำใดที่ผู้ใช้ที่ล็อกอินแล้วเข้าถึงได้
RBAC เป็นวิธีที่พบบ่อยที่สุดในการให้สิทธิ์ และมีความสำคัญ: ณ เดือนกันยายน 2026 Broken Access Control เป็นอันดับที่ 1 ใน OWASP Top 10:2025 ซึ่งเป็นรายการความเสี่ยงด้านความปลอดภัยของเว็บแอปพลิเคชันที่ร้ายแรงที่สุดที่ใช้กันอย่างแพร่หลาย ความผิดพลาดที่พบบ่อยคือผู้ใช้อ่านระเบียนของผู้ใช้อื่นโดยเปลี่ยน ID ใน URL หรือผู้ใช้ทั่วไปเรียก API เส้นทางที่สงวนไว้สำหรับผู้ดูแลระบบ
เลือกโมเดล
| โมเดล | วิธีทำงาน | เหมาะกับ | ข้อแลกเปลี่ยน |
|---|---|---|---|
| คอลัมน์บทบาทเดียว | users.role = admin / member | แอปขนาดเล็กที่มีสองหรือสามบทบาท | ยุ่งเหยิงเมื่อคนต้องการบทบาทต่างกันในที่ต่างกัน |
| บทบาทและสิทธิ์ | บทบาทจับคู่กับสิทธิ์ที่มีชื่อ (invoices.approve) และโค้ดตรวจสิทธิ์ | แอปที่กฎจะเติบโตขึ้น | มีตารางมากขึ้น และต้องดูแลรายการสิทธิ์ |
| บทบาทต่อทีม (multi-tenant) | ตารางสมาชิกภาพ: ผู้ใช้ + องค์กร + บทบาท | SaaS ที่คนอยู่ในทีมหรือพื้นที่ทำงาน | ทุกคิวรีต้องถูกจำกัดขอบเขตตามองค์กร |
| อิงแอตทริบิวต์หรือความสัมพันธ์ | กฎบนแอตทริบิวต์ของระเบียน ("ผู้เขียนแก้ไขโพสต์ของตัวเองได้") หรือบนความสัมพันธ์ (ถูกแชร์ให้) | เอกสาร การแชร์ เวิร์กโฟลว์ที่ซับซ้อน | เข้าใจยากกว่า ควรพิจารณาไลบรารีหรือบริการเฉพาะ |
| บทบาทจากผู้ให้บริการยืนยันตัวตน | ผู้ให้บริการล็อกอินเก็บบทบาทหรือการเป็นสมาชิกขององค์กร | แอปที่ใช้บริการยืนยันตัวตนแบบโฮสต์อยู่แล้ว | ผูกกับผู้ให้บริการนั้น และยังต้องบังคับใช้บนเซิร์ฟเวอร์ของคุณ |
ในทางปฏิบัติ แอปส่วนใหญ่ผสมสองอย่าง: บทบาท สำหรับว่าใครทำการประเภทใดได้ และ ความเป็นเจ้าของ สำหรับว่าใครทำกับระเบียนใดได้ "ผู้แก้ไขแก้ไขโพสต์ได้" เป็นกฎบทบาท ส่วน "...โพสต์ในพื้นที่ทำงานของตนเอง" เป็นกฎความเป็นเจ้าของ คุณต้องมีทั้งสองอย่าง
เริ่มให้ง่ายกว่าที่คิด ผู้ดูแลระบบ สมาชิก และผู้ดู ครอบคลุมแอปจำนวนมากได้อย่างน่าประหลาดใจ การแยกบทบาทภายหลังทำได้ง่ายกว่าการแก้ปมของสิบบทบาทที่ไม่มีใครเข้าใจ
ออกแบบบทบาทก่อนเขียนโค้ด
เขียนตารางสิทธิ์เป็นภาษาธรรมดาก่อน ตารางนี้ใช้เป็นสเปกให้ AI และเป็นแผนทดสอบของคุณไปพร้อมกัน:
| การกระทำ | เจ้าของ | ผู้ดูแลระบบ | สมาชิก | ผู้ดู |
|---|---|---|---|---|
| ดูโปรเจกต์ในทีม | ✓ | ✓ | ✓ | ✓ |
| สร้างและแก้ไขโปรเจกต์ | ✓ | ✓ | ✓ | |
| ลบโปรเจกต์ | ✓ | ✓ | ||
| เชิญและนำคนออก | ✓ | ✓ | ||
| เปลี่ยนบทบาท | ✓ | |||
| การเรียกเก็บเงิน | ✓ |
ตัดสินกรณีขอบด้วย: เจ้าของคนสุดท้ายออกจากทีมได้ไหม ผู้ดูแลระบบเอาเจ้าของออกได้ไหม ผู้ใช้ที่ถูกนำออกจะเห็นอะไร
ทำทีละขั้น
1. มีการล็อกอินที่ทำงานได้ก่อน
RBAC วางอยู่บนการยืนยันตัวตน เซิร์ฟเวอร์ต้องรู้อย่างเชื่อถือได้ว่าใครเป็นผู้ส่งแต่ละคำขอ จากเซสชันหรือโทเค็นที่ตรวจแล้ว ไม่ใช่จาก ID ของผู้ใช้ที่เบราว์เซอร์ส่งมาในเนื้อหาคำขอ
2. เก็บบทบาทในฐานข้อมูล
สำหรับแอปทีมเดียว คอลัมน์ role บนตารางผู้ใช้ก็เพียงพอ สำหรับทีม ใช้ตารางสมาชิกภาพ:
create table memberships (
user_id uuid references users(id),
organization_id uuid references organizations(id),
role text not null check (role in ('owner','admin','member','viewer')),
primary key (user_id, organization_id)
);
อย่าให้ผู้ใช้ตั้งบทบาทของตัวเองผ่านการอัปเดตโปรไฟล์ปกติ การเปลี่ยนบทบาทต้องผ่าน endpoint เฉพาะที่ตรวจสิทธิ์
3. ตรวจสิทธิ์บนเซิร์ฟเวอร์ ในที่เดียว
เขียนฟังก์ชันช่วยเพียงหนึ่งตัว เช่น can(user, 'projects.delete', project) แล้วเรียกใช้ในทุก API route, server action และงานเบื้องหลังที่อ่านหรือเปลี่ยนข้อมูล ฟังก์ชันกลางหนึ่งตัวตรวจทานและทดสอบได้ง่ายกว่าการตรวจกระจายอยู่ทั่วโค้ด ปฏิเสธเป็นค่าเริ่มต้น: ถ้าไม่มีกฎใดอนุญาตการกระทำนั้น ก็จะถูกปฏิเสธ
4. จำกัดขอบเขตทุกคิวรี
ทุกคิวรีข้อมูลของทีมควรรวมทีมเข้าไปด้วย: where organization_id = :currentOrg ไม่ใช่แค่ where id = :id นี่คือสิ่งที่หยุดไม่ให้ใครเปลี่ยน /projects/123 เป็น /projects/124 แล้วเห็นโปรเจกต์ของบริษัทอื่น สำหรับระเบียนของทีมอื่น ให้ตอบว่า "ไม่พบ" แทน "ไม่อนุญาต" เพื่อไม่เปิดเผยว่าระเบียนนั้นมีอยู่
5. เพิ่มการป้องกันระดับฐานข้อมูลในจุดที่สำคัญ
Row-level security ของ Postgres (RLS) ให้ฐานข้อมูลบังคับได้ว่าผู้ใช้แต่ละคนเห็นแถวใด สำคัญมากหากเบราว์เซอร์คุยกับฐานข้อมูลโดยตรง (เช่นในบริการแบ็กเอนด์แบบโฮสต์บางแห่ง) และเป็นชั้นที่สองที่มีประโยชน์สำหรับตารางที่ละเอียดอ่อนในกรณีอื่น
6. สะท้อนบทบาทในหน้าจอ
ตอนนี้ให้ซ่อนหรือปิดการใช้งานสิ่งที่ผู้ใช้ทำไม่ได้ เพื่อไม่ให้แอปสับสน สิ่งนี้เพื่อความใช้งานเท่านั้น การตรวจบนเซิร์ฟเวอร์เป็นสิ่งที่ปกป้องจริง
7. บันทึกการกระทำที่ละเอียดอ่อน
บันทึกว่าใครเปลี่ยนบทบาท ลบข้อมูล หรือส่งออกระเบียน และเมื่อใด บันทึกการตรวจสอบ (audit log) ช่วยตอบคำถามจากฝ่ายซัพพอร์ตและการจัดการเหตุการณ์
8. ทดสอบเหมือนผู้โจมตี
ล็อกอินเป็นแต่ละบทบาทและลองทุกการกระทำในตารางสิทธิ์ จากนั้นลองสิ่งที่ไม่ควรทำได้: เรียก API route ของผู้ดูแลระบบในฐานะผู้ดู เปลี่ยน ID ใน URL และคำขอ ลบตัวเองในฐานะเจ้าของคนสุดท้าย การเขียนการทดสอบอัตโนมัติสำหรับตารางสิทธิ์คุ้มค่าเมื่อกฎนิ่งแล้ว
พรอมต์ที่ได้ผล
เพิ่มการควบคุมการเข้าถึงตามบทบาท บทบาทต่อองค์กร: owner, admin, member, viewer เก็บในตารางสมาชิกภาพ สิทธิ์อยู่ในตารางนี้: [วางตารางของคุณ] สร้างฟังก์ชัน can(user, action, resource) หนึ่งตัวฝั่งเซิร์ฟเวอร์ ปฏิเสธเป็นค่าเริ่มต้น และใช้ในทุก API route จำกัดขอบเขตทุกคิวรีด้วย organization_id ซ่อนการกระทำที่ผู้ใช้ทำไม่ได้ในหน้าจอ แต่อย่าพึ่งสิ่งนั้นเพื่อความปลอดภัยเด็ดขาด
ตรวจทานทุก API route และฟังก์ชันฝั่งเซิร์ฟเวอร์ในแอปนี้ สำหรับแต่ละรายการ ให้ระบุว่าอ่านหรือเปลี่ยนข้อมูลอะไร ตรวจหรือไม่ว่าผู้ใช้ล็อกอินอยู่ ตรวจบทบาทหรือไม่ และจำกัดคิวรีตามองค์กรของผู้ใช้หรือไม่ แก้รายการที่ขาดการตรวจสอบ และแสดงรายการให้ฉันดู
เพิ่มหน้า /admin ที่เฉพาะผู้ดูแลระบบเข้าถึงได้ ซึ่งเจ้าของเปลี่ยนบทบาทของสมาชิกได้ ป้องกันการลบหรือลดระดับเจ้าของคนสุดท้าย บันทึกทุกการเปลี่ยนบทบาทพร้อมผู้ที่ทำและเวลา และตรวจว่า endpoint เปลี่ยนบทบาทปฏิเสธทุกคนที่ไม่ใช่เจ้าของ
ข้อผิดพลาดที่พบบ่อย
- ตรวจบทบาทเฉพาะฝั่งหน้าบ้าน API ยังเปิดให้ใครก็ได้ที่เรียกใช้
- เชื่อ ID จากฝั่งไคลเอนต์ เช่น
userIdหรือroleในเนื้อหาคำขอแทนที่จะดูจากเซสชัน - คิวรีที่ไม่จำกัดขอบเขต ที่ดึงข้อมูลด้วย ID ของระเบียนเพียงอย่างเดียว
- ตรวจบทบาทในหน้าแต่ไม่ตรวจใน API route การส่งออก การดาวน์โหลดไฟล์ หรือ webhook
- ผู้ดูแลระบบจากที่อยู่อีเมล ที่เขียนฝังไว้ในโค้ด (
if email === 'me@...') ซึ่งลืมง่ายและเปลี่ยนยาก - มีบทบาทมากเกินไปเร็วเกินไป ซึ่งไม่มีใครอธิบายได้อีกหกเดือนต่อมา
- เซสชันที่ค้าง: ผู้ใช้ที่ถูกลดระดับยังคงมีสิทธิ์ผู้ดูแลจนกว่าจะออกจากระบบ ให้ตรวจบทบาทจากฐานข้อมูลหรือรีเฟรชบทบาทสำหรับการกระทำที่ละเอียดอ่อน
เช็กลิสต์ RBAC
- เขียนตารางสิทธิ์เป็นภาษาธรรมดาและตัดสินกรณีขอบแล้ว
- เซิร์ฟเวอร์รู้ว่าผู้ใช้คือใครจากเซสชัน ไม่ใช่จากข้อมูลคำขอ
- บทบาทเก็บในฐานข้อมูล และผู้ใช้เปลี่ยนบทบาทของตัวเองไม่ได้
- มีการตรวจสิทธิ์กลางหนึ่งจุด ปฏิเสธเป็นค่าเริ่มต้น และใช้ในทุก route
- ทุกคิวรีถูกจำกัดขอบเขตตามองค์กรหรือความเป็นเจ้าของของผู้ใช้
- มี row-level security ในจุดที่เบราว์เซอร์เข้าถึงฐานข้อมูลได้
- หน้าจอซ่อนสิ่งที่ผู้ใช้ทำไม่ได้ (เพื่อความใช้งานเท่านั้น)
- การกระทำที่ละเอียดอ่อนถูกบันทึก
- ทดสอบทุกบทบาท รวมถึงการเรียก API โดยตรงและการเปลี่ยน ID
การควบคุมการเข้าถึงเป็นหนึ่งรายการในลิสต์ที่ยาวกว่า เช็กลิสต์ความปลอดภัยสำหรับแอปที่สร้างด้วย AI ครอบคลุมส่วนอื่นๆ ถ้าคุณกำลังสร้างเครื่องมือสำหรับทีมงานของคุณเอง วิธีสร้างเครื่องมือภายใน แสดงว่าบทบาทเข้ามาอยู่ตรงไหน
RBAC บน Mythex
Mythex ไม่มีระบบล็อกอินหรือบทบาทในตัวสำหรับผู้ใช้ของแอปคุณ คุณเลือกไลบรารีหรือผู้ให้บริการยืนยันตัวตน และอธิบายบทบาท จากนั้นเอเจนต์จะเชื่อมต่อกับโค้ดและฐานข้อมูล Postgres ของโปรเจกต์ (เพิ่มการล็อกอินในแอปของคุณ) บทบาทในพื้นที่ทำงานของ Mythex เอง เช่น Owner และ Member ควบคุมว่าใครแก้ไขโปรเจกต์ Mythex ของคุณได้ ไม่ใช่ใครใช้แอปที่คุณเผยแพร่ได้ ใช้โหมด Plan เพื่อตกลงตารางสิทธิ์ก่อนสร้าง จากนั้นใช้พรอมต์ตรวจทานด้านบน และทดสอบทุกบทบาทใน Preview ก่อนเผยแพร่ พรอมต์เรื่องความปลอดภัย ในเอกสารเป็นขั้นตอนต่อที่มีประโยชน์
คำถาม
การควบคุมการเข้าถึงตามบทบาทคืออะไร?
การควบคุมการเข้าถึงตามบทบาท (RBAC) กำหนดว่าผู้ใช้แต่ละคนทำอะไรได้บ้างตามบทบาทที่ตนถืออยู่ เช่น ผู้ดูแลระบบ ผู้แก้ไข หรือผู้ดู สิทธิ์จะผูกกับบทบาท ไม่ใช่กับบุคคลแต่ละคน ดังนั้นการเปลี่ยนสิ่งที่ใครทำได้จึงเป็นเพียงการเปลี่ยนบทบาทของเขา
การยืนยันตัวตนกับการให้สิทธิ์ต่างกันอย่างไร?
การยืนยันตัวตน (authentication) ตรวจว่าใครเป็นผู้ใช้ โดยปกติคือการล็อกอิน ส่วนการให้สิทธิ์ (authorisation) ตัดสินว่าผู้ที่ล็อกอินแล้วเห็นและทำอะไรได้บ้าง RBAC เป็นวิธีหนึ่งของการให้สิทธิ์
การซ่อนปุ่มในหน้าจอเพียงพอสำหรับการจำกัดการเข้าถึงหรือไม่?
ไม่เพียงพอ ใครก็สามารถเรียก API ของคุณโดยตรงได้ การซ่อนปุ่มจึงเปลี่ยนแค่สิ่งที่ผู้คนเห็น คำขอทุกคำที่อ่านหรือเปลี่ยนข้อมูลต้องถูกตรวจบนเซิร์ฟเวอร์เทียบกับบทบาทของผู้ใช้และความเป็นเจ้าของของระเบียนนั้น
ควรใช้ row-level security ของ Postgres หรือไม่?
Row-level security ให้ฐานข้อมูลบังคับเองว่าผู้ใช้แต่ละคนอ่านหรือเขียนแถวใดได้บ้าง ซึ่งเป็นตาข่ายนิรภัยที่แข็งแรง โดยเฉพาะเมื่อหน้าบ้านคุยกับฐานข้อมูลโดยตรง แต่มันเพิ่มความซับซ้อน แอปจำนวนมากจึงบังคับสิทธิ์ในโค้ดฝั่งเซิร์ฟเวอร์ และใช้ row-level security เป็นชั้นเพิ่มเติมสำหรับตารางที่ละเอียดอ่อน