Supabase ব্যবহার করার সময় সবচেয়ে গুরুত্বপূর্ণ বিষয়গুলোর একটি হলো Row Level Security (RLS)। অনেক নতুন ডেভেলপার টেবিল তৈরি করলেও RLS ঠিকভাবে কনফিগার না করার কারণে হয় ডেটা সবার জন্য উন্মুক্ত হয়ে যায়, নয়তো কোনো ব্যবহারকারীই ডেটা অ্যাক্সেস করতে পারে না। ফলাফল হিসেবে অ্যাপ্লিকেশনে বড় ধরনের Security Bug তৈরি হয়, যা পরবর্তীতে খুঁজে বের করা কঠিন হয়ে পড়ে।
এই সমস্যা এড়ানোর সবচেয়ে ভালো উপায় হলো শুরু থেকেই RLS-কে সঠিকভাবে বোঝা এবং প্রতিটি Table-এর জন্য প্রয়োজন অনুযায়ী Policy লেখা। এই ব্লগে আমরা একদম বেসিক থেকে শুরু করে ধাপে ধাপে RLS-এর প্রতিটি অংশ বিস্তারিতভাবে দেখব, যাতে আপনি নিজের প্রজেক্টে সরাসরি প্রয়োগ করতে পারেন।
এই ব্লগে আমরা জানবো—
- Row Level Security (RLS) কী এবং কেন এটি দরকার
- RLS Enable করার নিয়ম
- Policy কীভাবে কাজ করে
- SELECT, INSERT, UPDATE, DELETE Rule কীভাবে লিখতে হয়
- Authentication এর সাথে RLS কীভাবে যুক্ত হয়
- বাস্তব উদাহরণ দিয়ে সম্পূর্ণ একটি Blog System-এর Policy তৈরি
- সাধারণ ভুল ও Best Practices
Row Level Security (RLS) কী?
Row Level Security (RLS) হলো PostgreSQL-এর একটি বিল্ট-ইন নিরাপত্তা ব্যবস্থা, যা Supabase সরাসরি ব্যবহার করে। সাধারণত Database-এ আমরা Table পর্যায়ে Permission দিই—অর্থাৎ কোনো একজন User পুরো Table পড়তে পারবে কিনা, লিখতে পারবে কিনা। কিন্তু বাস্তব অ্যাপ্লিকেশনে এটি যথেষ্ট নয়। একজন User-এর শুধু নিজের ডেটা দেখা বা পরিবর্তন করা উচিত, অন্য কারো ডেটা নয়।
এখানেই RLS কাজে আসে। RLS-এর মাধ্যমে আপনি Table-এর প্রতিটি Row-এর জন্য আলাদাভাবে নিয়ন্ত্রণ করতে পারেন—
- কে কোন Row দেখতে পারবে (SELECT)
- কে নতুন Row যোগ করতে পারবে (INSERT)
- কে বিদ্যমান Row পরিবর্তন করতে পারবে (UPDATE)
- কে Row মুছে ফেলতে পারবে (DELETE)
অর্থাৎ Permission শুধুমাত্র Table পর্যায়ে নয়, প্রতিটি Row পর্যায়েও নিয়ন্ত্রণ করা যায়। নিচের টেবিলে চার ধরনের Operation-এর সংক্ষিপ্ত বর্ণনা দেওয়া হলো, যা RLS দিয়ে নিয়ন্ত্রণ করা যায়:
| Operation | কাজ | Policy Clause |
|---|---|---|
SELECT |
ডেটা পড়া/দেখা | USING |
INSERT |
নতুন Row যোগ করা | WITH CHECK |
UPDATE |
বিদ্যমান Row পরিবর্তন করা | USING / WITH CHECK |
DELETE |
Row মুছে ফেলা | USING |
কেন RLS দরকার?
ধরুন আপনার একটি posts নামের Table আছে, যেখানে প্রতিটি ইউজারের নিজস্ব Post সংরক্ষিত থাকে। নিচের টেবিলে এই Table-এর কয়েকটি নমুনা Row দেখানো হলো:
| id | user_id | title |
|---|---|---|
| 1 | User A | Hello |
| 2 | User B | My Post |
যদি এই Table-এ RLS Enable করা না থাকে, তাহলে User A সহজেই User B-এর Post দেখতে বা পরিবর্তন করতে পারবে, কারণ Database কোনো Row-ভিত্তিক নিয়ন্ত্রণ করছে না। এটি একটি গুরুতর Security ঝুঁকি, বিশেষ করে যেসব অ্যাপ্লিকেশনে ব্যক্তিগত তথ্য বা Sensitive Data থাকে।
কিন্তু RLS সঠিকভাবে ব্যবহার করলে—
- User A শুধুমাত্র নিজের Post দেখতে ও পরিবর্তন করতে পারবে।
- User B শুধুমাত্র নিজের Post দেখতে ও পরিবর্তন করতে পারবে।
- একজন User অন্য কারো Row সম্পর্কে জানতেও পারবে না, কারণ Database Level-এই তা Block হয়ে যাবে।
RLS Enable করা
Supabase-এ RLS Enable করার দুইটি সাধারণ উপায় আছে। প্রথমটি হলো Dashboard থেকে সরাসরি, দ্বিতীয়টি হলো SQL Editor দিয়ে সরাসরি Command চালিয়ে।
Supabase Dashboard থেকে—
Database → Table → Enable RLS
অথবা নিচের SQL Command ব্যবহার করে সরাসরি Enable করা যায়—
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
RLS Enable করার পর কোনো Policy না থাকলে কেউ ডেটা অ্যাক্সেস করতে পারবে না—এমনকি আপনি নিজেও নয়, যতক্ষণ না আপনি নিজে Policy লিখে দিচ্ছেন। এটাই Supabase-এর মূল Security Model: ডিফল্টভাবে সবকিছু বন্ধ, প্রয়োজন অনুযায়ী খুলে দিতে হয়।
Policy কী?
Policy হলো এক ধরনের Rule যা নির্ধারণ করে দেয়—
কে কী করতে পারবে।
প্রতিটি Policy একটি নির্দিষ্ট Table, একটি নির্দিষ্ট Operation (SELECT/INSERT/UPDATE/DELETE), এবং একটি Condition-এর সাথে যুক্ত থাকে। এই Condition সত্য হলেই কেবল সেই Operation সম্পন্ন হয়।
কয়েকটি সাধারণ উদাহরণ—
- সবাই Read করতে পারবে
- শুধু Login করা User Insert করতে পারবে
- User শুধুমাত্র নিজের Data Edit করতে পারবে
- শুধুমাত্র Admin সব Data দেখতে পারবে
Authentication
Supabase Auth ব্যবহার করে কোনো User Login করার পর, Supabase একটি বিশেষ Function সরবরাহ করে যার মাধ্যমে বর্তমান Logged-in User-কে চেনা যায়—
auth.uid()
এই Function বর্তমান Logged-in User-এর Unique ID (UUID) প্রদান করে। যেমন—
auth.uid() → 4be9b...
এটি RLS-এর সবচেয়ে বেশি ব্যবহৃত Function, কারণ প্রায় প্রতিটি Owner-Based Policy-তে auth.uid()-কে Row-এর user_id-এর সাথে তুলনা করা হয়।
SELECT Policy
ধরুন আপনি চান, একজন User শুধুমাত্র নিজের Post দেখতে পারবে, অন্য কারো নয়। এর জন্য নিচের মতো একটি Policy লিখতে হবে—
CREATE POLICY "Users can view own posts" ON posts FOR SELECT USING ( auth.uid() = user_id );
এখানে auth.uid() মানে বর্তমান Logged-in User, আর user_id মানে সেই Row-এর মালিক (Owner)। এই দুইটি মান সমান হলেই কেবল User সেই Row দেখতে পারবে; অন্যথায় Row-টি তার কাছে সম্পূর্ণ অদৃশ্য থাকবে।
INSERT Policy
এবার ধরুন, শুধুমাত্র Login করা User-ই নতুন Post যোগ করতে পারবে, Guest User নয়। এর জন্য Policy হবে এমন—
CREATE POLICY "Users can insert own posts" ON posts FOR INSERT WITH CHECK ( auth.uid() = user_id );
এখানে WITH CHECK নতুন Row Valid কিনা তা যাচাই করে। অর্থাৎ কোনো User যদি নিজের user_id ছাড়া অন্য কারো user_id দিয়ে Post তৈরি করার চেষ্টা করে, Database সেই Insert Request প্রত্যাখ্যান করবে।
UPDATE Policy
User যেন শুধুমাত্র নিজের Post-ই Update করতে পারে, অন্য কারো নয়, তার জন্য Policy হবে—
CREATE POLICY "Users update own posts" ON posts FOR UPDATE USING ( auth.uid() = user_id );
DELETE Policy
একইভাবে, User যেন শুধুমাত্র নিজের Post-ই Delete করতে পারে—
CREATE POLICY "Users delete own posts" ON posts FOR DELETE USING ( auth.uid() = user_id );
Public Read Policy
অনেক সময় Blog-এর মতো Application-এ আমরা চাই, সবাই (Login না করা User-ও) Post পড়তে পারুক, কিন্তু Edit বা Delete শুধুমাত্র Owner করতে পারবে। এমন ক্ষেত্রে SELECT Policy-তে true ব্যবহার করা যায়—
CREATE POLICY "Public read" ON posts FOR SELECT USING ( true );
এখানে true মানে Condition সবসময় সত্য, অর্থাৎ সকল User (Login করা বা না করা) যে কোনো Row Read করতে পারবে।
শুধুমাত্র Login User Read করতে পারবে
যদি আপনি চান শুধুমাত্র Login করা User-রাই ডেটা দেখতে পারবে, Guest User নয়, তাহলে auth.role() Function ব্যবহার করে এভাবে লেখা যায়—
USING ( auth.role() = 'authenticated' );
এতে শুধুমাত্র Login করা User-ই Data দেখতে পারবে। যেসব User Login করেননি (Anonymous User), তারা কোনো Row দেখতে পারবেন না—Database Level-এই তাদের জন্য Access সম্পূর্ণ বন্ধ থাকবে।
Admin Policy
অনেক Application-এ Admin User-দের জন্য বিশেষ Access প্রয়োজন হয়, যেমন সব User-এর Data দেখা বা পরিবর্তন করার ক্ষমতা। ধরুন profiles নামের একটি Table-এ is_admin নামের একটি Column আছে, যেখানে True/False মান থাকে। তাহলে Admin Policy লেখা যায় এভাবে—
USING (
EXISTS (
SELECT 1
FROM profiles
WHERE id = auth.uid()
AND is_admin = true
)
);
এই Policy মূলত যাচাই করে, বর্তমান Logged-in User-এর profiles Table-এ কোনো Row আছে কিনা যেখানে is_admin = true। থাকলে, তাকে Admin হিসেবে ধরা হবে এবং সে বিশেষ Access পাবে।
সবাই Insert করতে পারবে না
অনেক নতুন ডেভেলপার তাড়াহুড়ো করে বা সমস্যা সমাধানের জন্য নিচের মতো Policy লিখে ফেলেন—
WITH CHECK ( true );
এতে যে কেউ, এমনকি Anonymous User পর্যন্ত, Data Insert করতে পারবে। এটি সাময়িকভাবে Development বা Testing-এর সময় সুবিধাজনক মনে হলেও, Production Environment-এ এটি ব্যবহার করা কখনোই উচিত নয়, কারণ এতে যে কেউ আপনার Database-এ যেকোনো পরিমাণ Spam বা ক্ষতিকর Data ঢুকিয়ে দিতে পারবে।
Owner Based Security
RLS-এ সবচেয়ে বেশি ব্যবহৃত এবং সবচেয়ে গুরুত্বপূর্ণ Rule হলো—
auth.uid() = user_id
এটাই অধিকাংশ Application-এর Standard Policy, কারণ প্রায় সব ধরনের অ্যাপ্লিকেশনেই একজন User-কে শুধুমাত্র নিজের ডেটার Owner হিসেবে রাখতে হয়। যেমন—
- Blog
- Todo App
- Chat App
- Portfolio CMS
- Social Network
প্রায় সবখানেই এই একই Owner-Based Rule ব্যবহৃত হয়, শুধু Table এবং Column-এর নাম ভিন্ন হতে পারে।
Multiple Policy
একটি Table-এ একইসাথে একাধিক Policy থাকতে পারে, এবং প্রতিটি Operation-এর জন্য সাধারণত আলাদা Policy লেখা হয়—
SELECT INSERT UPDATE DELETE
প্রতিটির জন্য আলাদা Rule লেখার ফলে আপনি খুব সূক্ষ্মভাবে নিয়ন্ত্রণ করতে পারেন—যেমন সবাই Read করতে পারবে, কিন্তু শুধু Owner Update বা Delete করতে পারবে।
Policy Delete করা
কোনো Policy আর প্রয়োজন না হলে, বা ভুল হয়ে থাকলে, তা মুছে ফেলার জন্য নিচের Command ব্যবহার করা যায়—
DROP POLICY "Users update own posts" ON posts;
সব Policy দেখা
বর্তমানে কোন কোন Policy সক্রিয় আছে তা দেখতে চাইলে, PostgreSQL-এর System View pg_policies ব্যবহার করা যায়—
SELECT * FROM pg_policies;
বাস্তব উদাহরণ: একটি সম্পূর্ণ Blog System
ধরুন আপনার posts Table-এ নিচের Column গুলো আছে—
- id
- title
- content
- user_id
আপনি চান একটি সম্পূর্ণ, নিরাপদ Blog System তৈরি করতে, যেখানে নিচের নিয়মগুলো কার্যকর থাকবে। নিচের টেবিলে প্রতিটি নিয়ম এবং তার জন্য প্রয়োজনীয় Rule একসাথে দেখানো হলো—
| নিয়ম | Operation | Rule |
|---|---|---|
| ✅ সবাই Post পড়তে পারবে | SELECT | USING (true) |
| ✅ Login User Post লিখতে পারবে | INSERT | WITH CHECK (auth.uid() = user_id) |
| ✅ Owner Edit করতে পারবে | UPDATE | USING (auth.uid() = user_id) |
| ✅ Owner Delete করতে পারবে | DELETE | USING (auth.uid() = user_id) |
এভাবে চারটি ছোট ছোট Policy একত্রিত করে খুব সহজেই একটি নিরাপদ ও Production-Ready Blog System তৈরি করা যায়, যেখানে প্রতিটি User শুধুমাত্র নিজের Content নিয়ন্ত্রণ করতে পারবে, কিন্তু সবার Content Public-ভাবে পড়া যাবে।
সাধারণ ভুল
নতুন ডেভেলপারদের মধ্যে RLS নিয়ে কাজ করার সময় কিছু সাধারণ ভুল প্রায়ই দেখা যায়। নিচের টেবিলে সেই ভুলগুলো এবং কেন সেগুলো ঝুঁকিপূর্ণ, তা একসাথে দেখানো হলো—
| ভুল | কেন সমস্যা তৈরি করে |
|---|---|
| ❌ RLS Enable না করা | Table সম্পূর্ণ অরক্ষিত থাকে, যে কেউ সব ডেটা দেখতে ও পরিবর্তন করতে পারে |
| ❌ Policy না লেখা | RLS Enable করলেও Policy না থাকলে কেউ ডেটা অ্যাক্সেস করতে পারবে না, এমনকি Owner-ও নয় |
❌ WITH CHECK ব্যবহার না করা |
Insert-এর সময় Data Valid কিনা তা যাচাই হয় না, ফলে ভুল user_id দিয়ে Row তৈরি হতে পারে |
❌ auth.uid() এর পরিবর্তে Hard Code করা |
নির্দিষ্ট একটি User ID Hard Code করলে Policy সবার জন্য সঠিকভাবে কাজ করবে না |
❌ Public Table-এ true ব্যবহার করা |
Sensitive Table-এ (যেমন profiles বা payments) সবার জন্য Access খুলে দিলে বড় ধরনের Data Leak হতে পারে |
Best Practices
- সবসময় প্রতিটি Table-এ RLS Enable করুন, এমনকি সেটি Internal বা কম গুরুত্বপূর্ণ মনে হলেও।
- প্রতিটি Table-এর জন্য আলাদা এবং স্পষ্ট Policy লিখুন, একসাথে সব Operation-এর জন্য একটি সাধারণ Rule না লেখাই ভালো।
- যতটা সম্ভব Owner ভিত্তিক Access Control ব্যবহার করুন, অর্থাৎ
auth.uid() = user_idধরনের Rule। - Admin Policy আলাদাভাবে রাখুন এবং সাধারণ User Policy থেকে স্পষ্টভাবে আলাদা করুন।
- Production-এ
truePolicy ব্যবহার করার আগে ভালোভাবে যাচাই করুন যে সত্যিই এটি Public হওয়া দরকার কিনা। auth.uid()ব্যবহার করে সবসময় User Identity যাচাই করুন, কখনো ID Hard Code করবেন না।- Supabase Dashboard-এর Policy Tester ব্যবহার করে প্রতিটি Policy আলাদা আলাদা User দিয়ে পরীক্ষা করে নিন।
উপসংহার
Supabase-এর Row Level Security (RLS) আপনার অ্যাপ্লিকেশনের নিরাপত্তার মূল ভিত্তি। এটি PostgreSQL-এর শক্তিশালী Security Feature-এর উপর নির্মিত, যার মাধ্যমে আপনি খুব সহজেই নির্ধারণ করতে পারেন কে কোন ডেটা দেখতে, যোগ করতে, পরিবর্তন করতে বা মুছে ফেলতে পারবে।
শুরুতে RLS এবং Policy লেখা কিছুটা জটিল মনে হতে পারে, কিন্তু একবার এর মূল ধারণা—auth.uid(), USING এবং WITH CHECK—বুঝে গেলে বেশিরভাগ বাস্তব সমস্যার সমাধান এই কয়েকটি প্যাটার্ন দিয়েই করা সম্ভব। যদি শুরু থেকেই সঠিকভাবে RLS Policy তৈরি করেন, তাহলে আপনার Supabase অ্যাপ আরও নিরাপদ, নির্ভরযোগ্য এবং প্রোডাকশন-রেডি হবে।