FixVibe

// docs / baas security / supabase rls scanner

Supabase RLS 스캐너: 행 수준 보안이 누락되었거나 망가진 테이블 찾기

Supabase 기반 앱을 출시할 때 고객 데이터와 인터넷 사이에 서 있는 유일한 것은 행 수준 보안(RLS)입니다. AI 코딩 도구는 컴파일되고 출시되며 조용히 데이터를 유출시키는 RLS 모양의 코드를 생성합니다 — RLS가 활성화되지 않은 채 생성된 테이블, 읽지만 제한하지 않는 정책, 열을 자기 자신과 비교하는 술어. 이 기사는 Supabase RLS 스캐너가 외부에서 무엇을 증명할 수 있는지, 바이브 코딩된 앱에 나타나는 네 가지 손상 RLS 패턴, 그리고 1분 이내에 자신의 배포를 스캔하는 방법을 보여줍니다.

외부 RLS 스캔이 증명할 수 있는 것

수동 RLS 스캔은 Supabase가 https://[project].supabase.co/rest/v1/에 노출하는 PostgREST 엔드포인트에 대해 실행됩니다. 공개 가능한 anon 키 — 브라우저가 사용하는 것과 동일한 키 — 만 사용하며, 테이블 목록 메타데이터, 익명 읽기, 익명 쓰기를 탐사합니다. 사용자로 인증하지 않으며 서비스 역할 권한을 건드리지 않습니다. 그것이 할 수 있는 모든 것은 인터넷의 인증되지 않은 공격자도 할 수 있습니다.

데이터베이스 외부에서 스캐너는 다음을 높은 신뢰도로 확인할 수 있습니다:

  • 테이블에서 RLS가 비활성화되어 있습니다. RLS가 꺼져 있거나 정책이 허용할 때 PostgREST는 익명 SELECT에 대해 행을 반환합니다. 어느 경우든 발견입니다.
  • 익명 역할이 테이블을 나열할 수 있습니다. anon 키를 사용한 GET /rest/v1/anon 역할이 어떤 권한이라도 가진 모든 테이블의 OpenAPI 스키마를 반환합니다. AI 생성 앱은 스키마에 USAGE를 부여하고 모든 테이블에 SELECT를 부여하는 경우가 많아, RLS가 실제 읽기를 거부하더라도 전체 스키마 맵이 노출됩니다.
  • 익명 역할이 삽입할 수 있습니다. 열 모양을 추측하는 탐사용 POST는 RLS에 거부하는 INSERT 정책이 없으면 성공합니다 — SELECT가 잠겨 있어도.
  • 서비스 역할 키가 브라우저 번들에 있습니다. RLS와 인접: 스캐너가 JavaScript 번들에서 SUPABASE_SERVICE_ROLE_KEY 또는 role: service_role이 있는 JWT를 발견하면 RLS는 무의미합니다 — 그 키의 보유자는 모든 정책을 우회합니다.

외부 스캔이 증명할 수 없는 것

스캐너의 경계에 대해 솔직해지세요. 외부 RLS 스캔은 pg_policies 테이블, 마이그레이션 파일, 또는 어떤 정책의 정확한 술어도 읽을 수 없습니다. 블랙박스 동작에서 추론하므로 의도적으로 공개된 데이터(마케팅 뉴스레터 테이블, 공개 제품 카탈로그)로 밝혀진 발견을 보고할 때도 있습니다. 스캐너가 의도를 명확히 할 수 없을 때 FixVibe 리포트는 이를 중간 신뢰도로 표시합니다 — 테이블 이름을 검토하고 판단하세요.

AI 도구가 생성하는 네 가지 손상 RLS 패턴

Cursor, Claude Code, Lovable, 또는 Bolt를 Supabase에 향하게 하면 수천 개의 앱에서 동일한 네 가지 손상 RLS 패턴이 나타납니다. 각각 타입 검사를 통과하고 컴파일되며 출시됩니다:

패턴 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를 우회합니다.

sql
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE  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 정책.

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

패턴 3: 정책이 열을 자기 자신과 비교

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: SELECT에는 정책이 있지만 INSERT/UPDATE에는 없음

개발자가 읽기는 잠그지만 쓰기는 잊습니다. RLS 정책은 명령별입니다. FOR SELECT는 읽기만 보호합니다. 정책이 거부하지 않으면 익명 클라이언트는 여전히 INSERT할 수 있습니다. 수정: 명령별 정책을 작성하거나 명시적인 USINGWITH CHECK 절을 가진 FOR ALL을 사용하세요.

FixVibe Supabase RLS 스캐너 작동 방식

baas.supabase-rls 검사는 세 단계로 실행되며, 각 단계마다 명시적인 신뢰도 수준이 있습니다:

  1. 1단계 — 지문 채취. 스캐너는 배포된 앱을 크롤링하고, JavaScript 번들을 파싱하며, 런타임 구성에서 Supabase 프로젝트 URL과 anon 키를 추출합니다. DNS 추측도, 브루트포스도 없이 — 브라우저가 읽는 것을 읽습니다.
  2. 2단계 — 스키마 발견. anon 키를 사용한 단일 GET /rest/v1/은 anon 역할이 볼 수 있는 모든 테이블의 OpenAPI 스키마를 반환합니다. 이 단계에서 스캐너는 테이블 이름을 기록하지만 행 데이터를 읽지 않습니다.
  3. 3단계 — 읽기 및 쓰기 탐사. 발견된 각 테이블에 대해 스캐너는 limit=1을 가진 익명 SELECT를 한 번 발행합니다. 행이 반환되면 RLS는 허용적입니다. 스캐너는 거기서 멈춥니다 — 행을 열거하지 않고, 페이지를 매기지 않으며, 데이터를 수정하지 않습니다. INSERT 탐사는 확인된 도메인 소유권과 명시적인 옵트인 뒤에서 게이트되며, 확인되지 않은 대상에 대해 결코 실행되지 않습니다.

각 발견은 정확한 요청 URL, 응답 상태, 응답 모양(헤더 전용), 그리고 테이블 이름과 함께 제공됩니다. 발견 하단의 AI 수정 프롬프트는 Supabase SQL 편집기에서 실행하는 복사-붙여넣기 가능한 SQL 블록입니다.

스캐너가 무언가를 발견했을 때 할 일

모든 RLS 발견은 런타임 비상 사태입니다. 공개 PostgREST 엔드포인트는 몇 분 이내에 공격자에 의해 스캔됩니다. 교정 순서는 기계적입니다:

  1. 모든 테이블 감사. Supabase SQL 편집기에서 SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';을 실행하세요. rowsecurity = false인 모든 행이 문제입니다.
  2. 모든 공개 테이블에서 RLS 활성화. 생성되는 모든 테이블에서 ENABLE ROW LEVEL SECURITYFORCE ROW LEVEL SECURITY를 기본으로 — 마이그레이션 템플릿으로 만드세요.
  3. 명령별로 정책 작성. FOR ALL USING (true)를 사용하지 마세요. SELECT, INSERT, UPDATE, DELETE에 대해 명시적인 정책을 작성하세요 — 각각 auth.uid() 또는 auth.jwt()의 org-id 열로 범위 좁힘.
  4. 두 번째 계정으로 검증. 다른 사용자로 가입하고 REST API를 통해 다른 사용자의 레코드를 직접 읽으려고 시도하세요. 응답이 200이면 정책이 깨졌습니다.
  5. 재스캔. 수정을 적용한 후 동일한 URL에 대해 FixVibe 스캔을 다시 실행하세요. 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 페이지에 대해 보고합니다. SnykSemgrep은 정적 분석 도구입니다 — 리포지토리에서 RLS 호출이 누락된 마이그레이션 파일을 찾지만, 배포된 데이터베이스가 잘못 구성되었다는 것을 증명할 수 없습니다. FixVibe는 그 틈에 위치합니다: 수동, BaaS 인식, 공개 URL에서 인증되지 않은 공격자가 증명할 수 있는 것에 집중.

자주 묻는 질문

스캐너가 내 데이터를 읽거나 수정하나요?

아니요. 수동 스캔은 발견된 각 테이블당 최대 한 번의 SELECT ... limit=1을 발행하여 RLS가 익명 읽기를 허용하는지 확인합니다. 스캐너는 응답 모양을 기록하며, 행 내용은 기록하지 않습니다. INSERT, UPDATE, DELETE 탐사는 확인된 도메인 소유권 뒤에서 게이트되며, 확인되지 않은 대상에 대해 결코 실행되지 않습니다.

내 Supabase 프로젝트가 일시 중지되었거나 사용자 정의 도메인에 있는 경우에도 작동하나요?

일시 중지된 프로젝트는 모든 요청에 503을 반환합니다 — 스캐너는 프로젝트를 도달 불가능으로 보고합니다. 배포된 앱이 여전히 브라우저에서 Supabase 클라이언트 SDK를 로드하는 한, 사용자 정의 도메인은 작동합니다. 스캐너는 어느 쪽이든 번들에서 프로젝트 URL을 추출합니다.

anon 키가 교체되거나 공개 가능 키가 변경되면 어떻게 되나요?

스캔을 다시 실행하세요. 스캐너는 매번 실행 시 현재 번들에서 키를 다시 추출합니다. 교체는 이전 보고서만 무효화하며, 데이터베이스의 정책 상태에는 영향을 주지 않습니다.

스캐너가 새로운 Supabase 공개 가능 키 모델(<code>sb_publishable_*</code>)을 확인하나요?

예. 탐지기는 레거시 anon JWT와 더 새로운 sb_publishable_* 키를 모두 인식하고 동일하게 취급합니다 — 둘 다 공개되도록 의도되었으며 둘 다 RLS를 유일한 방어선으로 남깁니다.

다음 단계

프로덕션 URL에 대해 무료 FixVibe 스캔을 실행하세요 — baas.supabase-rls 검사는 무료 티어를 포함한 모든 플랜에서 활성화됩니다. Supabase 프로젝트에서 또 무엇이 유출될 수 있는지에 대한 더 깊은 내용은 JavaScript에 노출된 Supabase 서비스 역할 키Supabase 스토리지 버킷 보안 체크리스트를 참조하세요. 모든 BaaS 제공자에 걸친 포괄적 관점은 BaaS 설정 오류 스캐너를 읽으세요.

// baas 표면 스캔

다른 누군가가 발견하기 전에 열린 테이블을 찾으세요.

프로덕션 URL을 입력하세요. FixVibe는 앱이 통신하는 BaaS 제공자를 열거하고, 공개 엔드포인트의 지문을 채취하며, 인증되지 않은 클라이언트가 무엇을 읽거나 쓸 수 있는지 보고합니다. 무료, 설치 불필요, 카드 불필요.

  • 무료 티어 — 월 3회 스캔, 가입 시 카드 불필요.
  • 수동 BaaS 지문 채취 — 도메인 소유권 확인 불필요.
  • Supabase, Firebase, Clerk, Auth0, Appwrite 등.
  • 모든 발견에 AI 수정 프롬프트 — Cursor / Claude Code에 그대로 붙여넣기.
Supabase RLS 스캐너: 행 수준 보안이 누락되었거나 망가진 테이블 찾기 — Docs · FixVibe