← กลับไปบทความทั้งหมด
RLS คืออะไร? ทำไมใช้ Supabase แล้วต้องเปิดใช้งาน
พื้นฐานการเขียนโปรแกรม

RLS คืออะไร? ทำไมใช้ Supabase แล้วต้องเปิดใช้งาน

ทำความรู้จัก Row Level Security หรือ RLS ระบบควบคุมสิทธิ์ข้อมูลใน Supabase พร้อมตัวอย่าง Policy และวิธีป้องกันผู้ใช้เข้าถึงข้อมูลของคนอื่น

RLS คืออะไร และทำไมใช้ Supabase แล้วต้องเปิด?

Supabase ช่วยให้หน้า Front-end เช่น Next.js, React หรือ Flutter เชื่อมต่อฐานข้อมูลได้โดยตรง ทำให้เราสร้างเว็บและแอปได้รวดเร็วขึ้น โดยไม่จำเป็นต้องเขียน Backend API ขึ้นมาเองทุกส่วน

แต่ความสะดวกนี้มาพร้อมกับเรื่องสำคัญที่ผู้พัฒนาห้ามมองข้าม นั่นคือการกำหนดว่า

ผู้ใช้งานแต่ละคนสามารถเข้าถึงข้อมูลอะไรได้บ้าง?

ตัวอย่างเช่น ในระบบบันทึกรายรับ–รายจ่าย ผู้ใช้ A ควรเห็นเฉพาะรายการของตัวเอง และต้องไม่สามารถเปิดดู แก้ไข หรือลบรายการของผู้ใช้ B ได้

ระบบที่ทำหน้าที่ควบคุมเรื่องนี้ใน Supabase เรียกว่า Row Level Security หรือ RLS

RLS คืออะไร?

RLS ย่อมาจาก Row Level Security เป็นระบบรักษาความปลอดภัยของ PostgreSQL ที่ใช้กำหนดสิทธิ์การเข้าถึงข้อมูลในระดับแถวหรือ Record

แทนที่จะกำหนดเพียงว่า “ผู้ใช้งานอ่านตาราง transactions ได้หรือไม่” เราสามารถกำหนดเงื่อนไขเพิ่มเติมได้ว่า

  • อ่านได้เฉพาะรายการของตัวเอง

  • เพิ่มข้อมูลโดยใช้ user_id ของตัวเองเท่านั้น

  • แก้ไขได้เฉพาะรายการที่ตัวเองเป็นเจ้าของ

  • ลบข้อมูลของตัวเองได้ แต่ลบข้อมูลของคนอื่นไม่ได้

  • ผู้ที่ยังไม่เข้าสู่ระบบไม่สามารถเข้าถึงข้อมูลได้

พูดง่าย ๆ คือ RLS ทำหน้าที่คล้ายยามที่ยืนอยู่หน้าฐานข้อมูล ทุกครั้งที่มีคำขอเข้ามา ระบบจะตรวจสอบก่อนว่าผู้ใช้งานคนนี้ได้รับอนุญาตให้ดำเนินการกับข้อมูลแถวนั้นหรือไม่

ทำไมใช้ Supabase แล้วต้องเปิด RLS?

Supabase สามารถสร้าง Data API จากตารางในฐานข้อมูลให้โดยอัตโนมัติ หน้า Front-end จึงสามารถเรียกอ่าน เพิ่ม แก้ไข และลบข้อมูลผ่าน Supabase Client ได้โดยตรง

หากตารางที่เปิดผ่าน Data API ไม่มี RLS และ Role นั้นมีสิทธิ์เข้าถึงตาราง ผู้ไม่หวังดีอาจไม่จำเป็นต้องใช้งานผ่านหน้าจอที่เราสร้างไว้ เขาสามารถเรียก API โดยตรงและพยายามเข้าถึงข้อมูลในฐานข้อมูลได้

Supabase จึงแนะนำให้เปิด RLS บนทุกตารางที่เปิดให้เข้าถึงผ่าน Data API และสร้าง Policy เพื่อกำหนดสิทธิ์อย่างเหมาะสม Supabase Docs: Securing your API

ตัวอย่างปัญหาเมื่อไม่ได้เปิด RLS

สมมติว่าเรามีตาราง transactions สำหรับเก็บรายการรายรับ–รายจ่ายดังนี้

iduser_idtitleamount
1User Aค่าอาหาร120
2User Bเงินเดือน30,000
3User Aค่าเดินทาง80

แต่ถ้าไม่มีการควบคุมที่ฐานข้อมูล การซ่อนรายการของ User B ด้วยเงื่อนไขใน Front-end เพียงอย่างเดียวไม่เพียงพอ เพราะผู้ใช้อาจแก้ไข Request หรือเรียก API ด้วยตัวเองเพื่อขอข้อมูลรายการอื่น

สิ่งที่ควรทำคือกำหนด Policy ในฐานข้อมูลว่า

auth.uid() = user_id

เงื่อนไขนี้หมายความว่า ID ของผู้ใช้งานที่กำลังส่งคำขอต้องตรงกับ user_id ของข้อมูลแถวนั้น จึงจะได้รับอนุญาตให้เข้าถึง

RLS และ Policy แตกต่างกันอย่างไร?

สองคำนี้มักถูกใช้ร่วมกัน แต่มีหน้าที่ต่างกัน

  • RLS คือการเปิดระบบตรวจสอบสิทธิ์ระดับแถว

  • Policy คือกฎที่ระบุว่าใครสามารถทำอะไรกับข้อมูลแถวใดได้บ้าง

การเปิด RLS อย่างเดียวแต่ยังไม่ได้สร้าง Policy โดยทั่วไปจะทำให้ผู้ใช้งานผ่าน Publishable Key ไม่สามารถเข้าถึงข้อมูลในตารางนั้นได้ จนกว่าจะมี Policy อนุญาต Supabase Docs: Row Level Security

ดังนั้น ขั้นตอนที่ถูกต้องจึงประกอบด้วย:

  1. เปิด RLS

  2. กำหนดสิทธิ์ของ Role เท่าที่จำเป็น

  3. สร้าง Policy สำหรับแต่ละการทำงาน

  4. ทดสอบด้วยผู้ใช้หลายบัญชี

วิธีเปิด RLS ใน Supabase

เราสามารถเปิด RLS ผ่านหน้า Dashboard ของ Supabase หรือใช้คำสั่ง SQL

alter table public.transactions
enable row level security;


ตารางที่สร้างผ่าน Table Editor ใน Supabase Dashboard จะเปิด RLS ให้โดยปริยาย แต่ถ้าสร้างตารางผ่าน SQL Editor, Migration หรือเครื่องมืออื่น ควรเปิด RLS อย่างชัดเจน Supabase Docs: Securing your API

เพื่อให้การตั้งค่าเหมือนกันในทุก Environment ควรเก็บคำสั่งเปิด RLS และคำสั่งสร้าง Policy ไว้ใน Migration ด้วย

ตัวอย่าง RLS สำหรับระบบรายรับ–รายจ่าย

สมมติว่าเราสร้างตารางดังนี้

create table public.transactions (
  id bigint generated always as identity primary key,
  user_id uuid not null references auth.users(id),
  title text not null,
  amount numeric not null,
  created_at timestamptz not null default now()
);

จากนั้นเปิด RLS

alter table public.transactions
enable row level security;

Policy สำหรับอ่านข้อมูลของตัวเอง

create policy "Users can view their own transactions"
on public.transactions
for select
to authenticated
using (
  (select auth.uid()) = user_id
);


เมื่อผู้ใช้เรียกดูข้อมูล PostgreSQL จะคืนเฉพาะแถวที่มี user_id ตรงกับผู้ใช้ที่เข้าสู่ระบบอยู่

Policy สำหรับเพิ่มข้อมูลของตัวเอง

create policy "Users can insert their own transactions"
on public.transactions
for insert
to authenticated
with check (
  (select auth.uid()) = user_id
);


Policy นี้ป้องกันไม่ให้ User A ส่งข้อมูลใหม่โดยแอบใส่ user_id ของ User B

Policy สำหรับแก้ไขข้อมูลของตัวเอง

create policy "Users can update their own transactions"
on public.transactions
for update
to authenticated
using (
  (select auth.uid()) = user_id
)
with check (
  (select auth.uid()) = user_id
);


ในกรณีของ UPDATE:

  • using ตรวจสอบว่าผู้ใช้มีสิทธิ์เลือกแถวเดิมมาแก้ไขหรือไม่

  • with check ตรวจสอบว่าข้อมูลหลังแก้ไขยังเป็นไปตามเงื่อนไขหรือไม่

จึงช่วยป้องกันทั้งการแก้รายการของคนอื่น และการเปลี่ยน user_id ของรายการให้เป็นผู้ใช้อื่น

Policy สำหรับลบข้อมูลของตัวเอง

create policy "Users can delete their own transactions"
on public.transactions
for delete
to authenticated
using (
  (select auth.uid()) = user_id
);

หลังจากกำหนด Policy เหล่านี้ ผู้ใช้งานที่เข้าสู่ระบบจะสามารถจัดการเฉพาะข้อมูลของตัวเองได้

ทำไม NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY จึงเปิดเผยได้?

หลายคนเห็นคำว่า NEXT_PUBLIC แล้วกังวลว่า Supabase Key จะหลุดออกไปยัง Browser

ความจริงคือ Publishable Key ถูกออกแบบมาให้ใช้ใน Front-end เช่น เว็บไซต์ แอปมือถือ และโปรแกรมที่ผู้ใช้สามารถตรวจสอบไฟล์หรือ Source Code ได้อยู่แล้ว

ตัวอย่างการตั้งค่าใน Next.js:

NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxx

Publishable Key มีหน้าที่ระบุโปรเจกต์และเริ่มต้นการเชื่อมต่อ ไม่ได้ให้สิทธิ์ระดับผู้ดูแลระบบโดยตัวมันเอง

เมื่อผู้ใช้เข้าสู่ระบบผ่าน Supabase Auth ตัว Supabase Client จะส่ง Access Token ของผู้ใช้ไปพร้อมคำขอ จากนั้น PostgreSQL จะใช้ Role และข้อมูลประจำตัวของผู้ใช้ตรวจสอบกับ RLS Policy อีกครั้ง

ดังนั้น Publishable Key จึงสามารถอยู่ใน Front-end ได้ แต่ต้องใช้งานร่วมกับ RLS และสิทธิ์ที่จำกัดตามความจำเป็น Supabase Docs: API Keys

Publishable Key ไม่ได้ป้องกันฐานข้อมูลแทน RLS

สิ่งที่ควรเข้าใจคือ การเก็บ Publishable Key ไว้ในไฟล์ .env ไม่ได้ทำให้ Key นี้กลายเป็นความลับ

ถ้าตัวแปรมีคำนำหน้า NEXT_PUBLIC_ ค่าเหล่านี้จะถูกนำไปรวมอยู่ในโค้ดที่ส่งให้ Browser ผู้ใช้งานจึงสามารถตรวจสอบและค้นหาได้

เพราะฉะนั้น ความปลอดภัยต้องเกิดจาก:

  • การเปิด RLS

  • การเขียน Policy อย่างถูกต้อง

  • การให้สิทธิ์แก่ Role เท่าที่จำเป็น

  • การตรวจสอบผู้ใช้ผ่าน Supabase Auth

  • การตรวจสอบข้อมูลก่อนบันทึก

  • การทดสอบกรณีเข้าถึงข้อมูลข้ามบัญชี

ไม่ควรพึ่งการซ่อน Publishable Key เป็นกลไกหลักในการรักษาความปลอดภัย

Publishable Key กับ Secret Key แตกต่างกันอย่างไร?

ประเภท Keyตำแหน่งที่ควรใช้เปิดเผยสู่สาธารณะได้หรือไม่ผ่านการตรวจสอบ RLS หรือไม่
Publishable KeyFront-end, Mobile App และ Browserเปิดเผยได้ผ่าน RLS
Legacy Anon KeyFront-end ของโปรเจกต์รุ่นเดิมเปิดเผยได้ผ่าน RLS
Secret KeyBackend ที่เชื่อถือได้เท่านั้นห้ามเปิดเผยเด็ดขาดข้าม RLS (ไม่บังคับ RLS)
Legacy Service Role KeyBackend ที่เชื่อถือได้เท่านั้นห้ามเปิดเผยเด็ดขาดข้าม RLS (ไม่บังคับ RLS)

ตัวอย่างตัวแปรฝั่ง Server:

SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SECRET_KEY=sb_secret_xxx


ห้ามเติม NEXT_PUBLIC_ ให้ Secret Key เพราะอาจทำให้ Framework นำค่าไปฝังใน Client Bundle

เปิด RLS แล้วปลอดภัยทันทีหรือไม่?

คำตอบคือ ยังไม่ปลอดภัยทันที เพราะ RLS เป็นเพียงกลไก ส่วน Policy คือกฎที่เราต้องออกแบบให้ถูกต้อง

ตัวอย่าง Policy ที่อันตรายคือ

using (true)

เงื่อนไขนี้อนุญาตให้เข้าถึงทุกแถวที่ Policy ครอบคลุม หากนำไปใช้กับตารางข้อมูลส่วนตัวโดยไม่ตั้งใจ ข้อมูลของผู้ใช้ทุกคนอาจถูกเปิดเผยได้

นอกจากนี้ หากใช้ Policy แบบ for all โดยไม่ได้พิจารณาแยกตามการทำงาน อาจให้สิทธิ์มากเกินความจำเป็น

แนวทางที่ปลอดภัยกว่าคือสร้าง Policy แยกตามการทำงาน:

  • SELECT สำหรับอ่าน

  • INSERT สำหรับเพิ่ม

  • UPDATE สำหรับแก้ไข

  • DELETE สำหรับลบ

แล้วให้สิทธิ์เฉพาะ Role ที่จำเป็น เช่น authenticated แทนการเปิดให้ anon โดยไม่มีเหตุผล

RLS ใช้แทน Validation ได้หรือไม่?

RLS ทำหน้าที่ควบคุมว่าใครเข้าถึงข้อมูลแถวใดได้ แต่ไม่ควรใช้แทนการตรวจสอบความถูกต้องของข้อมูลทั้งหมด

ตัวอย่างเช่น ระบบรายรับ–รายจ่ายยังควรตรวจสอบว่า

  • จำนวนเงินต้องมากกว่าศูนย์

  • ชื่อรายการต้องไม่เป็นค่าว่าง

  • หมวดหมู่ต้องมีอยู่จริง

  • ประเภทต้องเป็นรายรับหรือรายจ่าย

  • วันที่ต้องอยู่ในรูปแบบที่ถูกต้อง

การป้องกันที่เหมาะสมควรมีหลายระดับ ได้แก่

  1. ตรวจสอบข้อมูลที่หน้า Front-end เพื่อให้ผู้ใช้ได้รับ Feedback ทันที

  2. ตรวจสอบที่ Server หรือ Database เพื่อป้องกันการข้ามหน้าเว็บ

  3. ใช้ Constraint รักษาความถูกต้องของข้อมูล

  4. ใช้ RLS ควบคุมสิทธิ์การเข้าถึงแต่ละแถว

กรณีใดบ้างที่ควรใช้ RLS?

ควรใช้ RLS เมื่อข้อมูลเกี่ยวข้องกับเจ้าของหรือสิทธิ์ของผู้ใช้งาน เช่น

  • รายการรายรับ–รายจ่าย

  • ข้อมูลส่วนตัว

  • เอกสารของผู้ใช้

  • คำสั่งซื้อ

  • โปรเจกต์ของสมาชิก

  • ข้อความส่วนตัว

  • ข้อมูลภายในองค์กร

  • ข้อมูลที่สมาชิกแต่ละ Role เห็นไม่เหมือนกัน

ส่วนข้อมูลสาธารณะ เช่น บทความที่เผยแพร่แล้ว อาจสร้าง Policy ให้อ่านได้โดยไม่ต้องเข้าสู่ระบบ แต่ยังควรจำกัดสิทธิ์เพิ่ม แก้ไข และลบไว้สำหรับผู้ดูแลหรือเจ้าของเนื้อหาเท่านั้น

Checklist ก่อนนำ Supabase ขึ้น Production

ก่อนเปิดระบบให้ผู้ใช้งานจริง ควรตรวจสอบอย่างน้อยดังนี้:

  • เปิด RLS บนทุกตารางที่เข้าถึงผ่าน Data API

  • มี Policy แยกสำหรับ SELECT, INSERT, UPDATE และ DELETE

  • ผู้ใช้เห็นเฉพาะข้อมูลที่ตัวเองมีสิทธิ์

  • ผู้ใช้ไม่สามารถส่ง user_id ของคนอื่นเพื่อเพิ่มข้อมูลได้

  • ผู้ใช้ไม่สามารถเปลี่ยนเจ้าของข้อมูลระหว่างการ Update

  • ผู้ที่ยังไม่เข้าสู่ระบบเข้าถึงได้เฉพาะข้อมูลที่ตั้งใจให้เป็นสาธารณะ

  • ไม่มี Secret Key หรือ Service Role Key อยู่ใน Front-end

  • ทดสอบด้วยบัญชีผู้ใช้อย่างน้อยสองบัญชี

  • ทดสอบการเรียก API โดยตรง ไม่ได้ทดสอบเฉพาะจากหน้าเว็บ

  • เก็บ RLS, Policy และ Grants ไว้ใน Migration

  • ทบทวน Policy ทุกครั้งที่เพิ่มตารางหรือฟีเจอร์ใหม่

สรุป

RLS คือระบบควบคุมสิทธิ์ระดับแถวของ PostgreSQL ที่ช่วยกำหนดว่าผู้ใช้งานแต่ละคนสามารถอ่าน เพิ่ม แก้ไข หรือลบข้อมูลใดได้บ้าง

เหตุผลที่ใช้ Supabase แล้วควรเปิด RLS เพราะ Front-end สามารถเชื่อมต่อกับ Data API ได้โดยตรง การซ่อนปุ่มหรือกรองข้อมูลเฉพาะบนหน้าเว็บจึงไม่เพียงพอ ผู้ใช้สามารถส่ง Request ไปยัง API ได้โดยไม่ผ่าน UI ของเรา

สำหรับระบบรายรับ–รายจ่าย Policy ที่สำคัญที่สุดคือการกำหนดให้ auth.uid() ตรงกับ user_id ของข้อมูล เพื่อให้ผู้ใช้จัดการได้เฉพาะรายการของตัวเอง

จำหลักการง่าย ๆ ได้ว่า:

Publishable Key เปิดเผยได้ แต่ข้อมูลต้องถูกป้องกันด้วย RLS
ส่วน Secret Key ต้องเก็บไว้ที่ Backend และห้ามเปิดเผยเด็ดขาด

การเปิด RLS อาจใช้เวลาเพิ่มขึ้นเล็กน้อยในช่วงพัฒนา แต่เป็นพื้นฐานสำคัญที่ช่วยป้องกันไม่ให้ข้อมูลของผู้ใช้งานแต่ละคนรั่วไหลถึงกันเมื่อระบบถูกนำไปใช้งานจริง

อ่านต่อเพื่อเชื่อมภาพให้ครบ

Supabase คืออะไร? ทำไมคนสร้างเว็บและแอปยุคใหม่ถึงนิยมใช้
ข้อมูลและระบบหลังบ้าน

Supabase คืออะไร? ทำไมคนสร้างเว็บและแอปยุคใหม่ถึงนิยมใช้

Supabase คือแพลตฟอร์ม Backend as a Service ที่ช่วยให้เราสร้างระบบหลังบ้านสำหรับเว็บและแอปได้ง่ายขึ้น โดยมีทั้งฐานข้อมูล PostgreSQL ระบบสมาชิก พื้นที่เก็บไฟล์ และ API พร้อมใช้งานในแพลตฟอร์มเดียว

8 มิ.ย. 25699 นาที
อยากทำ SaaS ตัวแรก ควรเริ่มจากตรงไหนดี?
ไอเดียและการต่อยอด

อยากทำ SaaS ตัวแรก ควรเริ่มจากตรงไหนดี?

สิ่งที่สำคัญที่สุด แนะนำให้ควรเริ่มจากเรื่องที่ตนเองสนใจ หรือเริ่มต้นจากปัญหาที่ตัวเองประสบพบเจอมาก่อน “ที่ไหนมีปัญหาที่นั้นย่อมมีเงิน”

25 มิ.ย. 256910 นาที