Skip to content

PostgreSQL:Grant

GRANT 와 RLS 의 관계

PostgreSQL에서 테이블 접근은 두 단계로 제어됩니다.

  1. 1단계: GRANT (테이블 수준 권한)
    • "이 역할이 이 테이블에 접근할 수 있는가?"
    • GRANT가 없으면 해당 역할은 테이블 자체에 접근할 수 없습니다. 문 앞에서 차단되는 것과 같습니다.
  2. 2단계: RLS (행 수준 권한)
    • "접근이 허용된 역할 중, 어떤 행을 볼/수정할 수 있는가?"
    • RLS는 GRANT를 통과한 후에 적용됩니다. 문 안에 들어온 사람 중 누가 어떤 방에 들어갈 수 있는지 정하는 것입니다.

Supabase의 역할 구조

  • anon - 로그인하지 않은 사용자 (API 키만으로 접근)
  • authenticated - 로그인한 사용자 (auth.uid()가 존재)
  • service_role - 서버 측 관리자 — RLS를 무시함

테이블에 GRANT가 없으면:

authenticated 사용자 → GRANT 없음 → 차단 (RLS까지 도달하지 못함)

GRANT를 추가하면:

authenticated 사용자 → GRANT 통과 → RLS 정책 평가 → 허용

각 권한의 의미

  GRANT SELECT,   -- 조회 (SELECT 쿼리)
        INSERT,   -- 삽입 (다른 관리자 추가)
        UPDATE,   -- 수정
        DELETE    -- 삭제
  ON public.my_table -- 테이블 이름
  TO authenticated;  -- 로그인한 사용자 역할에게

요약:

  • GRANT 없이 RLS만 → 아무도 접근 불가
  • GRANT만 있고 RLS 없음 → authenticated 전원이 모든 행에 접근 가능
  • GRANT + RLS → authenticated 중 정책을 만족하는 사용자만 접근 가능

테이블의 grant 가 뭔지 확인하는 방법

  SELECT grantee, privilege_type
  FROM information_schema.role_table_grants
  WHERE table_schema = 'public'
    AND table_name = 'admins';

GRANT가 없는 경우:

   grantee | privilege_type
  ---------+---------------
  (0 rows)

GRANT가 있는 경우:

      grantee     | privilege_type
  ----------------+---------------
   authenticated  | SELECT
   authenticated  | INSERT
   authenticated  | UPDATE
   authenticated  | DELETE

Supabase 의 GRANT 자동 적용

Supabase는 기본적으로 public 스키마에 다음과 같은 기본 권한이 설정되어 있습니다.

ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT ALL ON TABLES TO anon, authenticated;

이 설정 때문에 public에 새 테이블을 만들면 anon과 authenticated에 자동으로 GRANT가 적용됩니다.

보안 관점에서, 비로그인 사용자가 테이블에 접근할 이유가 없다면, anon 역할의 권한을 제거하는 것이 더 적절합니다.

REVOKE ALL ON public.admins FROM anon;

PUBLIC grantee

PostgreSQL의 모든 역할을 의미하는 가상 그룹입니다.

  • PUBLIC에 GRANT → 현재 + 미래의 모든 역할이 해당 권한을 가짐
  • PUBLIC에서 REVOKE → 그 기본 권한을 제거

PostgreSQL은 함수를 생성하면 자동으로 "GRANT EXECUTE TO PUBLIC" 을 적용합니다. 그래서 명시적으로 REVOKE 하지 않으면 anon 포함 누구나 실행할 수 있었던 것입니다.

EVOKE EXECUTE ON FUNCTION public.is_admin() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION public.is_admin() TO authenticated;