// docs / baas security / supabase rls scanner
Supabase RLS სკანერი: იპოვეთ ცხრილები გამოტოვებული ან გაუმართავი row-level security-ით
Row-level security (RLS) არის ერთადერთი რამ, რაც დგას თქვენი მომხმარებლების მონაცემებსა და ინტერნეტს შორის, როდესაც თქვენ Supabase-ზე დაფუძნებულ აპს უშვებთ. AI-კოდირების ხელსაწყოები ქმნიან RLS-ფორმის კოდს, რომელიც კომპილირდება, იშვება და მონაცემებს ჩუმად ჟონავს — ცხრილები შექმნილია RLS-ის ჩართვის გარეშე, policy-ები, რომლებიც კითხულობენ მაგრამ არასოდეს ზღუდავენ, პრედიკატები, რომლებიც სვეტს თავის თავს ადარებენ. ეს სტატია გვიჩვენებს, რის დამტკიცებაც შეუძლია Supabase RLS-სკანერს გარედან, ოთხ გაუმართავ-RLS-ის ფორმას, რომელიც vibe-coded აპებში ჩნდება და როგორ დავასკანეროთ თქვენი საკუთარი დეპლოიმენტი წუთზე ნაკლებში.
რის დამტკიცებაც შეუძლია გარე RLS-სკანს
პასიური RLS-სკანი მუშაობს PostgREST-ის endpoint-ის წინააღმდეგ, რომელსაც Supabase ავლენს https://[project].supabase.co/rest/v1/-ზე. ის იყენებს მხოლოდ გამოსაქვეყნებელ anon გასაღებს — იმავე გასაღებს, რომელსაც თქვენი ბრაუზერი იყენებს — და ამოწმებს ცხრილების სიის მეტამონაცემებს, ანონიმურ წაკითხვებსა და ანონიმურ ჩაწერებს. ის არასოდეს ავთენტიფიცირდება როგორც მომხმარებელი და არასოდეს ეხება service-role პრივილეგიებს. ყველაფერი, რის გაკეთებაც მას შეუძლია, შეუძლია ინტერნეტში არსებულ არაავთენტიფიცირებულ თავდამსხმელსაც.
მონაცემთა ბაზის გარედან, სკანერი მაღალი დარწმუნებით ადასტურებს შემდეგს:
- RLS ცხრილზე გათიშულია. PostgREST აბრუნებს მწკრივებს ანონიმური
SELECT-ისთვის, როცა RLS გათიშულია ან როცა policy ნებას რთავს. ნებისმიერ შემთხვევაში ეს აღმოჩენაა. - ანონიმურ როლს შეუძლია ცხრილების ჩამოთვლა.
GET /rest/v1/anon-გასაღებით აბრუნებს OpenAPI-სქემას ყოველი ცხრილისთვის, რომელზეცanonროლს რაიმე პრივილეგია აქვს. AI-გენერირებული აპები ხშირად ანიჭებენUSAGE-ს სქემაზე დაSELECT-ს ყოველ ცხრილზე, რაც ავლენს მთელ სქემის რუკას მაშინაც კი, როცა RLS აკრძალავს ფაქტობრივ წაკითხვებს. - ანონიმურ როლს შეუძლია ჩასმა. საცდელი
POSTსვეტის ფორმის ვარაუდით წარმატებული იქნება, თუ RLS-ს არ აქვსINSERT-policy, რომელიც მას უარყოფს — მაშინაც კი, თუSELECTჩაკეტილია. - service-role გასაღები ბრაუზერის ბანდლშია. RLS-თან მიმდებარედ: თუ სკანერი იპოვის
SUPABASE_SERVICE_ROLE_KEY-ს ან ნებისმიერ JWT-სrole: service_role-ით JavaScript-ბანდლში, RLS აზრს კარგავს — ამ გასაღების მფლობელი გვერდს უვლის ყოველ policy-ს.
რის დამტკიცებაც გარე სკანს არ შეუძლია
იყავით პატიოსანი სკანერის საზღვრებთან მიმართებაში. გარე RLS-სკანს არ შეუძლია წაიკითხოს თქვენი pg_policies ცხრილი, თქვენი მიგრაციის ფაილები ან ნებისმიერი policy-ის ზუსტი პრედიკატი. ის ასკვნის შავი ყუთის ქცევიდან, რაც ნიშნავს, რომ ის ხანდახან მოახსენებს აღმოჩენას, რომელიც გამოდის რომ განზრახ საჯარო მონაცემია (მარკეტინგული newsletter-ის ცხრილი, საჯარო პროდუქტის კატალოგი). FixVibe-ის რეპორტი მათ აღნიშნავს როგორც საშუალო დარწმუნების, როცა სკანერს არ შეუძლია განზრახვის გარჩევა — დაათვალიერეთ ცხრილის სახელი და გადაწყვიტეთ.
ოთხი გაუმართავი-RLS-ის ფორმა, რომელსაც AI-ხელსაწყოები ქმნიან
როდესაც Cursor-ს, Claude Code-ს, Lovable-ს ან Bolt-ს Supabase-ზე მიმართავთ, ათასობით აპში ერთი და იგივე ოთხი გაუმართავი-RLS-ის შაბლონი ჩნდება. თითოეული მათგანი გადის type-check-ს, კომპილირდება და იშვება:
ფორმა 1: RLS არასოდეს ჩაირთო
ყველაზე გავრცელებული შეცდომის რეჟიმი. მიგრაცია ქმნის ცხრილს, მაგრამ დეველოპერი (ან AI-ხელსაწყო) ივიწყებს 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-ს.
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;ფორმა 2: RLS ჩართულია, policy-ები არ არის
უფრო დახვეწილი შეცდომა. RLS ჩართულია, მაგრამ არც ერთი policy არ არის დაწერილი. ნაგულისხმევი ქცევა PostgreSQL-ში არის უარყოფა, ამიტომ ავთენტიფიცირებული მომხმარებლები ვერაფერს ხედავენ — და დეველოპერი ამატებს USING (true)-ს, რომ აპი იმუშაოს, რაც ყველას ნებას რთავს ყველაფერი წაიკითხოს. გასწორება: დაწერეთ policy, რომელიც auth.uid()-ით განსაზღვრავს არეალს: CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); და შესაბამისი INSERT/UPDATE/DELETE policy.
CREATE POLICY "select_own"
ON public.[name]
FOR SELECT
USING (auth.uid() = user_id);ფორმა 3: Policy სვეტს თავის თავს ადარებს
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.
ფორმა 4: Policy SELECT-ზე, მაგრამ არა INSERT/UPDATE-ზე
დეველოპერი ჩაკეტავს წაკითხვებს, მაგრამ ივიწყებს ჩაწერებს. RLS-policy-ები ბრძანებაზეა გათვლილი. FOR SELECT იცავს მხოლოდ წაკითხვებს; ანონიმურ კლიენტს მაინც შეუძლია INSERT-ი, თუ არც ერთი policy არ უარყოფს მას. გასწორება: დაწერეთ policy ბრძანებაზე ან გამოიყენეთ FOR ALL ცხადი USING და WITH CHECK კონსტრუქციებით.
როგორ მუშაობს FixVibe-ის Supabase RLS სკანერი
baas.supabase-rls შემოწმება მუშაობს სამ ეტაპად, თითოეული ცხადი დარწმუნების დონეებით:
- ეტაპი 1 — fingerprinting. სკანერი თვალს ადევნებს დეპლოიდ აპს, აანალიზებს მის JavaScript-ბანდლს და ამოიღებს Supabase-პროექტის URL-ს და anon-გასაღებს გაშვების კონფიგურაციიდან. არანაირი DNS-ვარაუდი, არანაირი brute force — ის კითხულობს იმას, რასაც ბრაუზერი კითხულობს.
- ეტაპი 2 — სქემის აღმოჩენა. ერთი
GET /rest/v1/anon-გასაღებით აბრუნებს OpenAPI-სქემას ყოველი ცხრილისთვის, რომელსაც anon-როლი ხედავს. სკანერი იწერს ცხრილების სახელებს, მაგრამ ამ ეტაპზე მწკრივების მონაცემებს არ კითხულობს. - ეტაპი 3 — წაკითხვისა და ჩაწერის შემოწმებები. ყოველი აღმოჩენილი ცხრილისთვის სკანერი უშვებს ერთ ანონიმურ
SELECT-სlimit=1-ით. თუ მწკრივები ბრუნდება, RLS დასაშვებია. სკანერი იქვე ჩერდება — ის არ ჩამოთვლის მწკრივებს, არ აქცევს ფურცლებად, არ ცვლის მონაცემებს. INSERT-შემოწმებები გადახდელია ვერიფიცირებული დომენის მფლობელობისა და ცხადი თანხმობის უკან; ისინი არასოდეს ეშვება არავერიფიცირებული მიზნების წინააღმდეგ.
ყოველი აღმოჩენა ცალ-ცალკე გადაეცემა ზუსტ მოთხოვნის URL-თან, პასუხის სტატუსთან, პასუხის ფორმასთან (მხოლოდ header) და ცხრილის სახელთან ერთად. AI-ფიქს-პრომპტი აღმოჩენის ბოლოში არის copy-paste SQL-ბლოკი, რომელსაც Supabase SQL-რედაქტორში გაუშვებთ.
რა უნდა გააკეთოთ, როცა სკანერი რამეს იპოვის
ყოველი RLS-აღმოჩენა საგანგებო რეჟიმის სიტუაციაა. საჯარო PostgREST-endpoint-ებს თავდამსხმელები წუთებში სკანერავენ. რემედიაციის თანამიმდევრობა მექანიკურია:
- აუდიტი გაუკეთეთ ყოველ ცხრილს. გაუშვით
SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';Supabase SQL-რედაქტორში. ნებისმიერი მწკრივიrowsecurity = false-ით პრობლემაა. - ჩართეთ RLS ყოველ საჯარო ცხრილზე. ნაგულისხმევად დააწესეთ
ENABLE ROW LEVEL SECURITYდაFORCE ROW LEVEL SECURITYყოველი შექმნილ ცხრილზე — გახადეთ ეს მიგრაციის შაბლონი. - დაწერეთ policy-ები ბრძანებაზე. არ გამოიყენოთ
FOR ALL USING (true). დაწერეთ ცხადი policy-ები SELECT-ის, INSERT-ის, UPDATE-ის, DELETE-ისთვის — თითოეულიauth.uid()-ით ანauth.jwt()-დან org-id-სვეტით განსაზღვრული. - დაამოწმეთ მეორე ანგარიშით. დარეგისტრირდით სხვა მომხმარებლად, სცადეთ წაიკითხოთ სხვა მომხმარებლის ჩანაწერები REST API-ით პირდაპირ. თუ პასუხი არის
200, policy გაუმართავია. - თავიდან დაასკანერეთ. გასწორების გამოყენების შემდეგ თავიდან გაუშვით FixVibe-სკანი იმავე URL-ის წინააღმდეგ.
baas.supabase-rlsაღმოჩენა უნდა გაიწმინდოს.
-- 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-ს ყოველ მოთხოვნაზე — სკანერი მოახსენებს პროექტს როგორც მიუღწევადს. კასტომ-დომენები მუშაობს, თუ დეპლოიდი აპი ჯერ კიდევ ჩატვირთავს Supabase-კლიენტ-SDK-ს ბრაუზერში; სკანერი პროექტის URL-ს ბანდლიდან ამოიღებს ნებისმიერ შემთხვევაში.
რა მოხდება, თუ ჩემი anon-გასაღები შეიცვალა ან ჩემი გამოსაქვეყნებელი გასაღები იცვლება?
თავიდან გაუშვით სკანი. სკანერი თავიდან ამოიღებს გასაღებს მიმდინარე ბანდლიდან ყოველი გაშვებისას. ცვლა აუქმებს მხოლოდ წინა რეპორტს, არა მონაცემთა ბაზის policy-მდგომარეობას.
სკანერი ამოწმებს თუ არა Supabase-ის ახალ გამოსაქვეყნებელ-გასაღების მოდელს (<code>sb_publishable_*</code>)?
კი. დეტექტორი ცნობს როგორც მემკვიდრე anon JWT-ებს, ასევე ახალ sb_publishable_* გასაღებებს და ეპყრობა მათ იდენტურად — ორივე გათვლილია იყოს საჯარო და ორივე ტოვებს RLS-ს, როგორც დაცვის ერთადერთ ხაზს.
შემდეგი ნაბიჯები
გაუშვით უფასო FixVibe-სკანი თქვენი პროდუქციის URL-ის წინააღმდეგ — baas.supabase-rls შემოწმება ჩართულია ყოველ გეგმაში, უფასო ტარიფის ჩათვლით. იმაზე უფრო ღრმად, რა შეიძლება ჟონავდეს Supabase-პროექტიდან, ნახეთ Supabase service role გასაღების გამოვლენა JavaScript-ში და Supabase storage-ბაკეტის უსაფრთხოების სია. ყველა BaaS-პროვაიდერზე ერთიანი ხედვისთვის, წაიკითხეთ BaaS-ის არასწორი კონფიგურაციის სკანერი.
