Skip to content

PostgreSQL:SecurityContext

PostgreSQL에서 View와 Function은 누구의 권한으로 실행되는가에 따라 두 가지 보안 모드를 가진다.

SECURITY DEFINER

권한은 객체 소유자(owner)로 적용됨. 그래서 RLS 가 ❌ 우회됨.

SECURITY INVOKER

권한은 호출자(caller)로 적용됨. 그래서 RLS 가 ✅ 적용됨.

SECURITY DEFINER vs SECURITY INVOKER

SECURITY DEFINER

SECURITY INVOKER

실행 권한

객체 소유자 (owner)

호출자 (caller)

RLS 적용

❌ 우회됨

✅ 적용됨

권한 검사

owner의 권한으로 통과

호출자가 해당 테이블 권한 필요

용도

권한 상승이 필요한 작업

일반적인 데이터 접근

비유

Unix의 setuid

일반 실행

View에서의 적용

View는 기본적으로 SECURITY DEFINER로 동작한다. PostgreSQL 15+에서 security_invoker 옵션이 추가되었다.

-- SECURITY DEFINER (기본값): owner 권한으로 실행, RLS 우회
CREATE VIEW my_view AS
SELECT * FROM my_table;

-- SECURITY INVOKER: 호출자 권한으로 실행, RLS 적용
CREATE VIEW my_view
WITH (security_invoker = true)
AS
SELECT * FROM my_table;

-- 기존 View 변경
ALTER VIEW my_view SET (security_invoker = true);

Function에서의 적용

Function은 기본적으로 SECURITY INVOKER로 동작한다. View와 기본값이 반대이므로 주의.

-- SECURITY INVOKER (기본값): 호출자 권한으로 실행
CREATE FUNCTION get_users()
RETURNS SETOF users
LANGUAGE sql
AS $$
    SELECT * FROM users;
$$;

-- SECURITY DEFINER: 함수 소유자 권한으로 실행 (권한 상승)
CREATE FUNCTION admin_get_all_users()
RETURNS SETOF users
LANGUAGE sql
SECURITY DEFINER
AS $$
    SELECT * FROM users;
$$;

객체

기본값

변경 방법

View

SECURITY DEFINER

WITH (security_invoker = true)

Function

SECURITY INVOKER

SECURITY DEFINER 키워드

WARNING

View와 Function의 기본값이 서로 반대이다. View는 기본이 DEFINER, Function은 기본이 INVOKER.

SECURITY DEFINER 사용 시 주의사항

SECURITY DEFINER는 권한 상승(privilege escalation)을 허용하므로 신중하게 사용해야 한다.

권장 사항:

  • search_path를 명시적으로 설정하여 schema injection 방지. 하단의 #search_path 항목 참조.
CREATE FUNCTION admin_action()
RETURNS void
LANGUAGE sql
SECURITY DEFINER
SET search_path = public
AS $$
    -- ...
$$;
  • 입력값 검증을 철저히 할 것 — 호출자가 owner 권한을 간접적으로 악용할 수 있음
  • 가능하면 SECURITY INVOKER를 우선 사용하고, 꼭 필요한 경우에만 DEFINER를 사용

search_path

schema injection 방지

SET search_path = '' 를 사용하여 schema injection 방지.

이 것은 SECURITY DEFINER 함수의 보안 취약점 방지를 위한 설정입니다.

SECURITY DEFINER 함수는 함수 소유자(보통 superuser) 권한으로 실행됩니다. 만약 악의적인 사용자가 search_path를 조작하면:

-- 공격자가 자신의 스키마에 가짜 admins 테이블을 만듦
CREATE SCHEMA attacker;
CREATE TABLE attacker.admins AS SELECT auth.uid() AS user_id;
SET search_path = attacker, public;

-- 이제 is_admin()이 attacker.admins를 참조하게 됨 → 항상 TRUE

해결 방법:

SET search_path = ''

search_path를 비우면 함수 내부에서 반드시 public.admins, auth.uid() 처럼 스키마를 명시해야 하므로, 외부에서 search_path를 아무리 바꿔도 항상 의도한 테이블을 참조합니다.

요약하면, SECURITY DEFINER + SET search_path = ''는 세트로 사용하는 보안 패턴입니다.

Supabase에서의 활용

Supabase는 RLS 기반 보안 모델을 사용하므로:

  • View: 반드시 security_invoker = true를 설정해야 RLS가 적용됨
  • Function (SECURITY DEFINER): RLS를 우회하는 "서비스 레벨" 작업에 활용 (예: 관리자 전용 함수, 트리거 내부 로직)
  • Function (SECURITY INVOKER): 일반 사용자 대상 함수에 사용, RLS가 자연스럽게 적용됨

자세한 내용은 Supabase:Database:RLS 참고.

See also