DEV Community

Tanakrit Seangnet
Tanakrit Seangnet

Posted on

🚀 ทำความเข้าใจ Row-Level Security (RLS) ใน PostgreSQL แบบง่ายที่สุด

Row-Level Security (RLS) เป็นหนึ่งในฟีเจอร์สำคัญของ PostgreSQL ที่ช่วยให้ระบบสามารถควบคุมการเข้าถึงข้อมูล ระดับแถว ได้อย่างปลอดภัย โดยเฉพาะระบบที่มีผู้ใช้หลายกลุ่ม เช่น โรงพยาบาล, องค์กร, หรือ Multi-tenant application

บทความนี้จะอธิบาย

  • RLS คืออะไร
  • สิทธิของ superuser / owner ส่งผลยังไง
  • ตัวอย่างผลลัพธ์ “ก่อน” และ “หลัง” ใช้ RLS

📌 Row-Level Security (RLS) คืออะไร?

RLS = การกรองข้อมูลระดับแถวโดยอัตโนมัติ ตามสิทธิของ user

PostgreSQL จะตรวจสอบทุกคำสั่ง SELECT, UPDATE, DELETE, INSERT

แล้วแสดงเฉพาะแถวที่ผ่าน policy ที่เรากำหนดไว้ เช่น:

  • เห็นเฉพาะข้อมูลของโรงพยาบาลตนเอง
  • เห็นเฉพาะข้อมูลที่ active
  • ห้ามแก้ไขข้อมูลที่ถูกซ่อน
  • ห้ามลบข้อมูลที่ไม่ใช่เจ้าของ

สามารถคิดง่ายๆ ว่า เป็นเหมือน WHERE อัตโนมัติที่ซ่อนอยู่ในระบบ

และ PostgreSQL จะใช้มันเองเสมอเมื่อมีการ Query


📌 สิทธิที่เกี่ยวข้องกับ RLS

✔ 1. Superuser

  • ไม่ถูกบังคับด้วย RLS
  • มองเห็นทุกเรคคอร์ดเหมือนปิด RLS อยู่
  • ไม่ต้องมี policy ก็เห็นทุกข้อมูล

superuser = bypass RLS


✔ 2. Owner ของ table

ค่าเริ่มต้น owner ก็ ไม่โดน RLS เช่นกัน

  • owner = select ได้ทุกแถว
  • owner = update/delete ได้ทุกแถว
  • แม้เปิด RLS แล้วก็ตาม

ถ้าอยากให้ owner ต้องทำตาม policy ต้องใช้:

ALTER TABLE your_table FORCE ROW LEVEL SECURITY;
Enter fullscreen mode Exit fullscreen mode

หลังจากใช้ FORCE:

  • owner ต้องทำตาม policy เช่นเดียวกับ user คนอื่น
  • ยกเว้น superuser ที่ยัง bypass อยู่

✔ 3. User ปกติ (non-owner)

เป็นกลุ่มที่ RLS จะมีผลจริง

  • ถ้าเปิด RLS แต่ไม่มี policy → เห็น 0 rows
  • ถ้ามี policy → เห็นเฉพาะข้อมูลที่ตรง policy

📌 วิธีเปิด / ปิด RLS

เปิด RLS:

ALTER TABLE t_order ENABLE ROW LEVEL SECURITY;
Enter fullscreen mode Exit fullscreen mode

ปิด RLS:

ALTER TABLE t_order DISABLE ROW LEVEL SECURITY;
Enter fullscreen mode Exit fullscreen mode

📘 ตัวอย่างเข้าใจง่ายที่สุด

สมมติเรามี table:

CREATE TABLE t_order (
   id SERIAL PRIMARY KEY,
   hospital_code TEXT,
   patient_name TEXT
);

INSERT INTO t_order (hospital_code, patient_name) VALUES
('A', 'Alice'),
('A', 'Amy'),
('B', 'Bob'),
('C', 'Chris');
Enter fullscreen mode Exit fullscreen mode

User ที่เข้าถึงระบบ:

  • userA → โรงพยาบาล A
  • userB → โรงพยาบาล B

🟦 ก่อนเปิด RLS (ไม่ใช้ RLS)

ผู้ใช้ทั้งสองเห็นเหมือนกันหมด

userA:

SELECT * FROM t_order;
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์:

id hospital_code patient_name
1 A Alice
2 A Amy
3 B Bob
4 C Chris

userB:

ได้ผลเหมือนกันทุกอย่าง

➡ ทุกคนเห็นทุกข้อมูล


🟩 เปิดใช้งาน RLS

เปิด RLS:

ALTER TABLE t_order ENABLE ROW LEVEL SECURITY;
Enter fullscreen mode Exit fullscreen mode

สร้าง policy จำกัดให้เห็นเฉพาะโรงพยาบาลของตน:

CREATE POLICY order_filter ON t_order
    FOR SELECT
    USING (hospital_code = current_setting('myapp.hospital_code'));
Enter fullscreen mode Exit fullscreen mode

ก่อนสั่ง query ให้ตั้งค่าตาม user


🔍 ผลลัพธ์เมื่อเปิด RLS

👉 userA:

SET myapp.hospital_code = 'A';
SELECT * FROM t_order;
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์:

id hospital_code patient_name
1 A Alice
2 A Amy

👉 userB:

SET myapp.hospital_code = 'B';
SELECT * FROM t_order;
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์:

id hospital_code patient_name
3 B Bob

🔥 สรุปผลลัพธ์แบบเทียบกันชัดๆ

❌ ไม่ใช้ RLS

User ข้อมูลที่เห็น
userA ทุกแถว
userB ทุกแถว

✅ ใช้ RLS

User ข้อมูลที่เห็น
userA เฉพาะโรงพยาบาล A
userB เฉพาะโรงพยาบาล B

⚠ หมายเหตุสำคัญ

หากเปิด RLS แต่ ไม่มี policy

ผู้ใช้ปกติจะเหมือนว่า table ว่างทันที:

SELECT * FROM t_order;
Enter fullscreen mode Exit fullscreen mode

ผล:

0 rows
Enter fullscreen mode Exit fullscreen mode

แต่ superuser และ owner ยังเห็นได้ (ถ้าไม่ FORCE)


📌 สรุปทั้งหมด

  • RLS คือฟีเจอร์ควบคุมการเห็นข้อมูลระดับแถว
  • Superuser และ Owner (โดย default) จะ bypass RLS
  • User ปกติจะถูกบังคับใช้ RLS
  • เปิด RLS แล้ว ต้องมี policy ไม่งั้นเห็น 0 rows
  • ใช้ RLS เพื่อทำระบบ multi-tenant หรือแบ่งสิทธิข้อมูลได้อย่างปลอดภัยมาก

Top comments (0)