// docs / baas security / supabase rls scanner
Supabase RLS 스캐너: 행 수준 보안이 누락되었거나 망가진 테이블 찾기
Supabase 기반 앱을 출시할 때 고객 데이터와 인터넷 사이에 서 있는 유일한 것은 행 수준 보안(RLS)입니다. AI 코딩 도구는 컴파일되고 출시되며 조용히 데이터를 유출시키는 RLS 모양의 코드를 생성합니다 — RLS가 활성화되지 않은 채 생성된 테이블, 읽지만 제한하지 않는 정책, 열을 자기 자신과 비교하는 술어. 이 기사는 Supabase RLS 스캐너가 외부에서 무엇을 증명할 수 있는지, 바이브 코딩된 앱에 나타나는 네 가지 손상 RLS 패턴, 그리고 1분 이내에 자신의 배포를 스캔하는 방법을 보여줍니다.
외부 RLS 스캔이 증명할 수 있는 것
A passive RLS scan looks at your Supabase project the way an anonymous visitor would. It uses only the publishable anon key — the same key your browser uses — and never authenticates as a user or touches service-role privileges. Anything it can see, an unauthenticated attacker on the internet can see.
데이터베이스 외부에서 스캐너는 다음을 높은 신뢰도로 확인할 수 있습니다:
- 테이블에서 RLS가 비활성화되어 있습니다. RLS가 꺼져 있거나 정책이 허용할 때 PostgREST는 익명
SELECT에 대해 행을 반환합니다. 어느 경우든 발견입니다. - 익명 역할이 테이블을 나열할 수 있습니다. anon 키를 사용한
GET /rest/v1/은anon역할이 어떤 권한이라도 가진 모든 테이블의 OpenAPI 스키마를 반환합니다. AI 생성 앱은 스키마에USAGE를 부여하고 모든 테이블에SELECT를 부여하는 경우가 많아, RLS가 실제 읽기를 거부하더라도 전체 스키마 맵이 노출됩니다. - Read access does not establish write safety. A passive scan cannot prove that INSERT, UPDATE, or DELETE policies protect every user and tenant. Review write policies and test them with isolated fixtures under the intended roles.
- 서비스 역할 키가 브라우저 번들에 있습니다. RLS와 인접: 스캐너가 JavaScript 번들에서
SUPABASE_SERVICE_ROLE_KEY또는role: service_role이 있는 JWT를 발견하면 RLS는 무의미합니다 — 그 키의 보유자는 모든 정책을 우회합니다.
외부 스캔이 증명할 수 없는 것
스캐너의 경계에 대해 솔직해지세요. 외부 RLS 스캔은 pg_policies 테이블, 마이그레이션 파일, 또는 어떤 정책의 정확한 술어도 읽을 수 없습니다. 블랙박스 동작에서 추론하므로 의도적으로 공개된 데이터(마케팅 뉴스레터 테이블, 공개 제품 카탈로그)로 밝혀진 발견을 보고할 때도 있습니다. 스캐너가 의도를 명확히 할 수 없을 때 FixVibe 리포트는 이를 중간 신뢰도로 표시합니다 — 테이블 이름을 검토하고 판단하세요.
AI 도구가 생성하는 네 가지 손상 RLS 패턴
When you point Cursor, Claude Code, Lovable, or Bolt at Supabase, the same four broken-RLS patterns come up again and again. Each one passes type-check, compiles, and ships:
패턴 1: RLS가 결코 활성화되지 않음
The most common failure mode. The migration creates the table but the developer (or the AI tool) forgets ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST happily serves the entire table to anyone with the anon key. Fix: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;, then add a policy per command. Enabling RLS with no policies denies all access through the API; the policies decide who gets in.
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;패턴 2: RLS는 활성화되었지만 정책이 없음
더 미묘한 실패. RLS는 활성화되었지만 정책이 작성되지 않습니다. PostgreSQL의 기본은 거부이므로 인증된 사용자는 아무것도 보지 못합니다 — 그래서 개발자는 앱이 동작하도록 USING (true)를 추가하고, 이는 모든 사람이 모든 것을 읽도록 허용합니다. 수정: auth.uid()로 범위를 좁힌 정책 작성: CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); 그리고 일치하는 INSERT/UPDATE/DELETE 정책.
CREATE POLICY "select_own"
ON public.[name]
FOR SELECT
USING (auth.uid() = user_id);패턴 3: 정책이 열을 자기 자신과 비교
복사-붙여넣기 흔적. 개발자가 USING (auth.uid() = user_id) 대신 USING (user_id = user_id) — 항상 참 — 를 작성합니다. 타입 검사 통과; 정책은 모든 행을 허용합니다. 수정: 항상 열을 함수 호출(auth.uid(), auth.jwt()->>'org_id' 등)과 비교하고, 결코 자기 자신이나 상수와 비교하지 마세요.
Shape 4: Overly permissive write policies
With RLS enabled, a role with no applicable policy is denied by default. A SELECT policy does not itself allow INSERT. Write exposure can arise from a permissive applicable INSERT or ALL policy, disabled RLS, or a role that bypasses RLS. Fix: inspect table privileges and every applicable policy, use ownership-scoped WITH CHECK conditions, and test writes with separate user identities.
FixVibe Supabase RLS 스캐너 작동 방식
FixVibe's Supabase check works from the deployed app, in three steps:
- Find the project. FixVibe loads the deployed app and reads the Supabase project URL and public key from the same configuration your browser receives. No guessing, no brute force.
- See what is exposed. FixVibe checks which tables the anonymous role can see, without reading row data at this step.
- Stage 3 — inspect anonymous read access. The passive check reports observable access to discovered resources. It does not create, update, or delete database rows, and it cannot establish that all write policies or cross-user boundaries are correct.
Each finding ships with the exact request URL, response status, response shape (header-only), and the table name. FixVibe now labels whether the fix belongs in code/config, a provider console, DNS, secret rotation, or manual review so the next step matches who can actually apply it.
스캐너가 무언가를 발견했을 때 할 일
모든 RLS 발견은 런타임 비상 사태입니다. 공개 PostgREST 엔드포인트는 몇 분 이내에 공격자에 의해 스캔됩니다. 교정 순서는 기계적입니다:
- 모든 테이블 감사. Supabase SQL 편집기에서
SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';을 실행하세요.rowsecurity = false인 모든 행이 문제입니다. - Enable RLS on every public table. Default to
ENABLE ROW LEVEL SECURITYon every table created — make it a migration template. - 명령별로 정책 작성.
FOR ALL USING (true)를 사용하지 마세요. SELECT, INSERT, UPDATE, DELETE에 대해 명시적인 정책을 작성하세요 — 각각auth.uid()또는auth.jwt()의 org-id 열로 범위 좁힘. - 두 번째 계정으로 검증. 다른 사용자로 가입하고 REST API를 통해 다른 사용자의 레코드를 직접 읽으려고 시도하세요. 응답이
200이면 정책이 깨졌습니다. - Re-scan. After applying the fix, re-run a FixVibe scan against the same URL. The Supabase RLS finding should clear.
-- 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에서 인증되지 않은 공격자가 증명할 수 있는 것에 집중.
자주 묻는 질문
스캐너가 내 데이터를 읽거나 수정하나요?
The passive RLS check is read-only. It examines observable anonymous read access and reports limited evidence. It does not perform database write operations or prove that every user and tenant is correctly isolated.
내 Supabase 프로젝트가 일시 중지되었거나 사용자 정의 도메인에 있는 경우에도 작동하나요?
일시 중지된 프로젝트는 모든 요청에 503을 반환합니다 — 스캐너는 프로젝트를 도달 불가능으로 보고합니다. 배포된 앱이 여전히 브라우저에서 Supabase 클라이언트 SDK를 로드하는 한, 사용자 정의 도메인은 작동합니다. 스캐너는 어느 쪽이든 번들에서 프로젝트 URL을 추출합니다.
anon 키가 교체되거나 공개 가능 키가 변경되면 어떻게 되나요?
스캔을 다시 실행하세요. 스캐너는 매번 실행 시 현재 번들에서 키를 다시 추출합니다. 교체는 이전 보고서만 무효화하며, 데이터베이스의 정책 상태에는 영향을 주지 않습니다.
스캐너가 새로운 Supabase 공개 가능 키 모델(sb_publishable_*)을 확인하나요?
예. 탐지기는 레거시 anon JWT와 더 새로운 sb_publishable_* 키를 모두 인식하고 동일하게 취급합니다 — 둘 다 공개되도록 의도되었으며 둘 다 RLS를 유일한 방어선으로 남깁니다.
다음 단계
Run a free FixVibe scan against your production URL — the Supabase RLS check runs on every plan including the free tier. For a deeper read on what else can leak from a Supabase project, see Supabase service role key exposed in JavaScript and Supabase storage bucket security checklist. For the umbrella view across all BaaS providers, read BaaS misconfiguration scanner.
