FixVibe

// docs / baas security / supabase rls scanner

اسکنر Supabase RLS: یافتن جدول‌هایی با امنیت در سطح ردیف ناموجود یا معیوب

امنیت در سطح ردیف (RLS) تنها چیزی است که میان داده‌های مشتریان شما و اینترنت قرار می‌گیرد وقتی یک اپلیکیشن مبتنی بر Supabase را منتشر می‌کنید. ابزارهای کدنویسی هوش مصنوعی کدی با شکل RLS تولید می‌کنند که کامپایل می‌شود، منتشر می‌گردد و بی‌سروصدا داده‌ها را افشا می‌کند — جدول‌هایی که بدون فعال‌سازی RLS ایجاد شده‌اند، سیاست‌هایی که می‌خوانند ولی هرگز محدود نمی‌کنند، پیش‌فرض‌هایی که یک ستون را با خودش مقایسه می‌کنند. این مقاله نشان می‌دهد یک اسکنر Supabase RLS از بیرون چه چیزی را می‌تواند اثبات کند، چهار شکل RLS معیوب که در اپلیکیشن‌های vibe-coded ظاهر می‌شود، و چگونه می‌توانید استقرار خود را در کمتر از یک دقیقه اسکن کنید.

یک اسکن خارجی RLS چه چیزی می‌تواند اثبات کند

یک اسکن غیرفعال RLS روی نقطه پایانی PostgREST اجرا می‌شود که Supabase در https://[project].supabase.co/rest/v1/ در دسترس قرار می‌دهد. تنها از کلید عمومی anon استفاده می‌کند — همان کلیدی که مرورگر شما به کار می‌برد — و فهرست متادیتای جدول‌ها، خواندن‌های ناشناس و نوشتن‌های ناشناس را بررسی می‌کند. هرگز به‌عنوان یک کاربر احراز هویت نمی‌شود و هرگز به امتیازات service-role دست نمی‌زند. هر آنچه می‌تواند انجام دهد، هر مهاجم بدون احراز هویت در اینترنت هم می‌تواند انجام دهد.

از بیرون پایگاه‌داده، یک اسکنر می‌تواند موارد زیر را با اطمینان بالا تأیید کند:

  • RLS روی یک جدول غیرفعال است. هنگامی که RLS خاموش باشد یا یک سیاست اجازه دهد، PostgREST برای یک SELECT ناشناس ردیف‌ها را برمی‌گرداند. هر دو حالت یک یافته محسوب می‌شوند.
  • نقش ناشناس می‌تواند جدول‌ها را فهرست کند. یک درخواست GET /rest/v1/ با کلید anon اسکیمای OpenAPI را برای هر جدولی که نقش anon روی آن هر گونه امتیازی دارد بازمی‌گرداند. اپلیکیشن‌های تولیدشده توسط هوش مصنوعی اغلب USAGE روی اسکیما و SELECT روی هر جدول را اعطا می‌کنند که نقشه کامل اسکیما را افشا می‌کند حتی وقتی RLS خواندن‌های واقعی را رد می‌کند.
  • نقش ناشناس می‌تواند درج کند. یک POST آزمایشی با حدسی درباره شکل ستون موفق خواهد شد اگر RLS سیاست INSERT برای رد آن نداشته باشد — حتی اگر SELECT قفل شده باشد.
  • کلید service-role در بسته مرورگر قرار دارد. در کنار RLS: اگر یک اسکنر SUPABASE_SERVICE_ROLE_KEY یا هر JWT با role: service_role را در بسته جاوااسکریپت پیدا کند، RLS بی‌معنا می‌شود — دارنده آن کلید همه سیاست‌ها را دور می‌زند.

یک اسکن خارجی چه چیزی را نمی‌تواند اثبات کند

درباره مرزهای اسکنر صادق باشید. یک اسکن خارجی RLS نمی‌تواند جدول pg_policies شما، فایل‌های مهاجرت یا گزاره دقیق هیچ سیاستی را بخواند. این اسکن از رفتار جعبه‌سیاه استنتاج می‌کند، یعنی گاهی یک یافته گزارش می‌دهد که در نهایت داده عمومی عمدی است (یک جدول خبرنامه بازاریابی، یک کاتالوگ محصول عمومی). گزارش FixVibe این موارد را زمانی که اسکنر نمی‌تواند نیت را تشخیص دهد به‌عنوان اطمینان متوسط علامت‌گذاری می‌کند — نام جدول را بررسی کنید و تصمیم بگیرید.

چهار شکل RLS معیوب که ابزارهای هوش مصنوعی تولید می‌کنند

وقتی Cursor، Claude Code، Lovable یا Bolt را به Supabase وصل می‌کنید، همان چهار الگوی معیوب RLS در هزاران اپلیکیشن ظاهر می‌شوند. هر یک از آن‌ها از تایپ‌چک عبور می‌کند، کامپایل می‌شود و منتشر می‌گردد:

شکل ۱: RLS هرگز فعال نشده

رایج‌ترین حالت شکست. مهاجرت جدول را ایجاد می‌کند ولی توسعه‌دهنده (یا ابزار هوش مصنوعی) ALTER TABLE ... ENABLE ROW LEVEL SECURITY را فراموش می‌کند. PostgREST با خوشحالی کل جدول را به هر کسی که کلید anon دارد ارائه می‌دهد. راه‌حل: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY; ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;. FORCE اختیاری نیست — بدون آن مالک جدول (و هر نقشی که مالکیت جدول را دارد) RLS را دور می‌زند.

sql
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE  ROW LEVEL SECURITY;

شکل ۲: RLS فعال، بدون سیاست

یک حالت شکست ظریف‌تر. RLS فعال است ولی هیچ سیاستی نوشته نشده است. پیش‌فرض در PostgreSQL رد است، پس کاربران احراز هویت‌شده هیچ چیزی نمی‌بینند — و توسعه‌دهنده USING (true) اضافه می‌کند تا اپلیکیشن کار کند، که به همه اجازه می‌دهد همه چیز را بخوانند. راه‌حل: سیاستی بنویسید که با auth.uid() محدود شود: CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); و یک سیاست متناظر INSERT/UPDATE/DELETE.

sql
CREATE POLICY "select_own"
  ON public.[name]
  FOR SELECT
  USING (auth.uid() = user_id);

شکل ۳: سیاستی که ستون را با خودش مقایسه می‌کند

A copy-paste artefact. The developer writes <code>USING (user_id = user_id)</code> — which is always true — instead of <code>USING (auth.uid() = user_id)</code>. Type-checks pass; the policy permits every row. <strong>Fix:</strong> always compare a column to a function call (<code>auth.uid()</code>, <code>auth.jwt()->>'org_id'</code>, etc.), never to itself or to a constant.

شکل ۴: سیاست روی SELECT ولی نه روی INSERT/UPDATE

توسعه‌دهنده خواندن‌ها را قفل می‌کند ولی نوشتن‌ها را فراموش می‌کند. سیاست‌های RLS به‌ازای هر دستور هستند. FOR SELECT فقط از خواندن‌ها محافظت می‌کند؛ یک کلاینت ناشناس همچنان می‌تواند INSERT کند اگر سیاستی آن را رد نکند. راه‌حل: برای هر دستور یک سیاست بنویسید، یا از FOR ALL با عبارات صریح USING و WITH CHECK استفاده کنید.

اسکنر Supabase RLS در FixVibe چگونه کار می‌کند

فحص baas.supabase-rls در سه مرحله اجرا می‌شود، هر یک با سطوح اطمینان صریح:

  1. مرحله ۱ — اثرانگشت‌برداری. اسکنر اپلیکیشن منتشرشده را پیمایش می‌کند، بسته جاوااسکریپت آن را تجزیه می‌کند و آدرس پروژه Supabase و کلید anon را از پیکربندی زمان اجرا استخراج می‌کند. بدون حدس DNS، بدون brute force — همان چیزی را می‌خواند که مرورگر می‌خواند.
  2. مرحله ۲ — کشف اسکیما. یک GET /rest/v1/ با کلید anon اسکیمای OpenAPI را برای هر جدولی که نقش anon می‌تواند ببیند بازمی‌گرداند. اسکنر نام جدول‌ها را ثبت می‌کند ولی در این مرحله داده‌های ردیف را نمی‌خواند.
  3. مرحله ۳ — پروب‌های خواندن و نوشتن. برای هر جدول کشف‌شده، اسکنر یک SELECT ناشناس با limit=1 ارسال می‌کند. اگر ردیف بازگردد، RLS بازگذاشته است. اسکنر همان‌جا متوقف می‌شود — ردیف‌ها را شمارش نمی‌کند، صفحه‌بندی نمی‌کند، داده را تغییر نمی‌دهد. پروب‌های INSERT پشت تأیید مالکیت دامنه و انتخاب صریح کاربر قفل شده‌اند؛ هرگز روی اهداف تأییدنشده اجرا نمی‌شوند.

هر یافته به همراه آدرس دقیق درخواست، وضعیت پاسخ، شکل پاسخ (فقط هدر) و نام جدول ارائه می‌شود. پرامپت اصلاحی هوش مصنوعی در پایان یافته یک بلوک SQL آماده copy-paste است که در ویرایشگر SQL در Supabase اجرا می‌کنید.

وقتی اسکنر چیزی پیدا کرد چه باید کرد

هر یافته RLS یک اضطرار زمان اجرا است. نقاط پایانی عمومی PostgREST در چند دقیقه توسط مهاجمان اسکن می‌شوند. توالی برطرف کردن مکانیکی است:

  1. هر جدول را بررسی کنید. SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; را در ویرایشگر SQL در Supabase اجرا کنید. هر ردیفی با rowsecurity = false یک مشکل است.
  2. RLS را روی هر جدول عمومی فعال کنید. پیش‌فرض را ENABLE ROW LEVEL SECURITY و FORCE ROW LEVEL SECURITY روی هر جدول ایجادشده قرار دهید — آن را یک قالب مهاجرت کنید.
  3. سیاست‌ها را دستور به دستور بنویسید. از FOR ALL USING (true) استفاده نکنید. سیاست‌های صریح برای SELECT، INSERT، UPDATE، DELETE بنویسید — هر یک محدود به auth.uid() یا یک ستون org-id از auth.jwt().
  4. با یک اکانت دوم تأیید کنید. به‌عنوان کاربر دیگری ثبت‌نام کنید، تلاش کنید رکوردهای کاربر دیگر را مستقیماً از طریق REST API بخوانید. اگر پاسخ 200 بود، سیاست معیوب است.
  5. دوباره اسکن کنید. پس از اعمال اصلاح، یک اسکن FixVibe را روی همان آدرس URL مجدداً اجرا کنید. یافته baas.supabase-rls باید پاک شود.
sql
-- Audit every table for missing RLS. Run in the Supabase SQL editor.
SELECT schemaname, tablename, rowsecurity
FROM   pg_tables
WHERE  schemaname = 'public'
ORDER  BY rowsecurity, tablename;

این چگونه با سایر اسکنرها مقایسه می‌شود

بیشتر ابزارهای عمومی DAST (Burp Suite، OWASP ZAP، Nessus) نمی‌دانند PostgREST چیست. آن‌ها اپلیکیشن شما را پیمایش می‌کنند، مسیر /rest/v1/ را نادیده می‌گیرند و درباره صفحات HTML که می‌فهمند گزارش می‌دهند. Snyk و Semgrep ابزارهای تحلیل ایستا هستند — فایل‌های مهاجرت بدون فراخوانی RLS را در ریپوی شما پیدا می‌کنند ولی نمی‌توانند اثبات کنند پایگاه‌داده مستقرشده پیکربندی نادرستی دارد. FixVibe در این شکاف می‌نشیند: غیرفعال، آگاه از BaaS و متمرکز بر آنچه یک مهاجم بدون احراز هویت می‌تواند از URL عمومی اثبات کند.

سؤالات متداول

آیا اسکنر داده‌های مرا می‌خواند یا تغییر می‌دهد؟

خیر. اسکن‌های غیرفعال حداکثر یک SELECT ... limit=1 به‌ازای هر جدول کشف‌شده ارسال می‌کنند تا تأیید کنند آیا RLS اجازه خواندن ناشناس می‌دهد یا نه. اسکنر شکل پاسخ را ثبت می‌کند، نه محتوای ردیف. پروب‌های INSERT، UPDATE و DELETE پشت تأیید مالکیت دامنه قفل شده‌اند و هرگز روی اهداف تأییدنشده اجرا نمی‌شوند.

اگر پروژه Supabase من متوقف شده یا روی یک دامنه سفارشی باشد، این کار می‌کند؟

پروژه‌های متوقف‌شده روی هر درخواست 503 برمی‌گردانند — اسکنر پروژه را به‌عنوان غیرقابل‌دسترسی گزارش می‌دهد. دامنه‌های سفارشی تا زمانی که اپلیکیشن مستقرشده همچنان SDK کلاینت Supabase را در مرورگر بارگذاری کند کار می‌کنند؛ اسکنر آدرس پروژه را به هر صورت از بسته استخراج می‌کند.

اگر کلید anon من چرخش داده شود یا کلید عمومی من تغییر کند چه؟

اسکن را دوباره اجرا کنید. اسکنر در هر اجرا کلید را دوباره از بسته فعلی استخراج می‌کند. چرخش فقط گزارش قبلی را بی‌اعتبار می‌کند، نه وضعیت سیاست‌های پایگاه‌داده را.

آیا اسکنر مدل کلید عمومی جدید Supabase (<code>sb_publishable_*</code>) را بررسی می‌کند؟

بله. شناساگر هم JWTهای قدیمی anon و هم کلیدهای جدیدتر sb_publishable_* را تشخیص می‌دهد و با هر دو یکسان رفتار می‌کند — هر دو قرار است عمومی باشند و هر دو RLS را به‌عنوان تنها خط دفاعی باقی می‌گذارند.

گام‌های بعدی

یک اسکن رایگان FixVibe را روی URL تولیدی خود اجرا کنید — فحص baas.supabase-rls در همه پلن‌ها از جمله پلن رایگان فعال است. برای مطالعه عمیق‌تر درباره آنچه از یک پروژه Supabase می‌تواند نشت کند، افشای کلید service role Supabase در جاوااسکریپت و چک‌لیست امنیت سطل ذخیره‌سازی Supabase را ببینید. برای دیدگاه چتری در میان همه ارائه‌دهندگان BaaS، اسکنر پیکربندی نادرست BaaS را بخوانید.

// سطح baas خود را اسکن کنید

جدول باز را پیش از دیگران پیدا کنید.

یک آدرس URL از محیط تولید وارد کنید. FixVibe ارائه‌دهندگان BaaS که اپلیکیشن شما با آن‌ها صحبت می‌کند را شناسایی می‌کند، نقاط پایانی عمومی آن‌ها را اثرانگشت‌برداری می‌کند و گزارش می‌دهد که یک کلاینت بدون احراز هویت چه چیزی می‌تواند بخواند یا بنویسد. رایگان، بدون نصب، بدون کارت اعتباری.

  • پلن رایگان — ۳ اسکن در ماه، بدون نیاز به کارت اعتباری برای ثبت‌نام.
  • اثرانگشت‌برداری غیرفعال BaaS — نیازی به تأیید دامنه نیست.
  • Supabase، Firebase، Clerk، Auth0، Appwrite و بیشتر.
  • پرامپت‌های اصلاحی هوش مصنوعی روی هر یافته — کافی است در Cursor / Claude Code جای‌گذاری کنید.
یک اسکن رایگان BaaS اجرا کنید

نیازی به ثبت‌نام نیست

اسکنر Supabase RLS: یافتن جدول‌هایی با امنیت در سطح ردیف ناموجود یا معیوب — Docs · FixVibe