Row-Level Security (RLS)는 PostgreSQL에서 사용자의 권한에 따라 특정 행(row)의 접근을 제한하는 기능입니다. 일반적인 테이블 수준의 접근 제어(GRANT, REVOKE)와 달리, 특정 조건을 만족하는 행만 조회/수정/삭제 가능하도록 설정할 수 있습니다.
RLS 개념 및 동작 방식
RLS의 주요 특징
- 행 단위 보안: 특정 테이블 내에서도 개별 행(row)마다 접근 권한을 다르게 설정 가능.
- 정책 기반 접근 제어: SQL 정책(
pg_policy
)을 생성하여 행별 접근 제어 규칙을 설정. - 슈퍼유저도 기본적으로 제한 가능:
FORCE ROW LEVEL SECURITY
를 사용하면, 슈퍼유저도 RLS를 우회할 수 없음. - 사용자/역할(Role) 기반 정책 적용 가능.
RLS 적용 시나리오
- 멀티 테넌트(Multi-Tenant) 환경에서 개별 고객 데이터 분리.
- 특정 부서 또는 팀만 특정 데이터를 조회/수정 가능하도록 제한.
- 개별 사용자가 자신의 데이터만 접근할 수 있도록 설정.
RLS 설정 및 관리
기본적인 RLS 활성화 및 비활성화
RLS는 기본적으로 비활성화되어 있으며, 테이블 단위로 설정할 수 있습니다.
-- RLS 활성화
ALTER TABLE roles ENABLE ROW LEVEL SECURITY;
-- RLS 비활성화
ALTER TABLE roles DISABLE ROW LEVEL SECURITY;
🔹 확인 방법
SELECT relname, relrowsecurity, relforcerowsecurity
FROM pg_class
WHERE relname = 'roles';
relrowsecurity
=true
: RLS 활성화됨.relforcerowsecurity
=true
: 슈퍼유저도 우회 불가능.
RLS 정책 생성 및 활용
기본적인 RLS 정책 추가
RLS는 특정 테이블에 대해 정책(Policy)을 추가하여 제어합니다.
CREATE POLICY policy_name
ON table_name
FOR { SELECT | INSERT | UPDATE | DELETE }
TO { PUBLIC | 특정 사용자 | 특정 ROLE }
USING (조건);
기본적인 예제
사용자 본인 데이터만 조회 가능하도록 설정
CREATE POLICY user_data_policy
ON users
FOR SELECT
USING (id = current_user::text);
🔹 정책 해석
FOR SELECT
: 조회(SELECT
)만 제한.USING (id = current_user::text)
:id
값이 현재 로그인한 사용자(current_user
)와 같은 경우만 허용.
RLS 정책 종류 및 예제
SELECT 제한
CREATE POLICY restrict_select
ON orders
FOR SELECT
TO public
USING (customer_id = current_user::text);
🔹 해석
orders
테이블에서customer_id
가 현재 로그인한 사용자인 경우만 조회 가능.
INSERT 제한
CREATE POLICY allow_insert
ON transactions
FOR INSERT
TO app_users
WITH CHECK (amount > 0);
🔹 해석
app_users
그룹의 사용자는amount > 0
인 데이터만 삽입 가능.
UPDATE 제한
CREATE POLICY allow_update
ON orders
FOR UPDATE
TO managers
USING (status != 'completed');
🔹 해석
managers
역할을 가진 사용자는status
가completed
가 아닌 경우에만 수정 가능.
DELETE 제한
CREATE POLICY allow_delete
ON logs
FOR DELETE
TO admins
USING (created_at < now() - interval '30 days');
🔹 해석
admins
역할을 가진 사용자만 30일이 지난logs
데이터를 삭제 가능.
RLS 정책 확인 및 삭제
현재 테이블의 RLS 정책 확인
SELECT polname, polpermissive, polcmd, polqual, polwithcheck
FROM pg_policy
WHERE polrelid = 'orders'::regclass;
RLS 정책 삭제
DROP POLICY restrict_select ON orders;
슈퍼유저 및 RLS 강제 적용
- PostgreSQL에서 기본적으로 슈퍼유저는 RLS를 우회할 수 있습니다.
- 이를 차단하려면 강제 RLS 모드(FORCE ROW LEVEL SECURITY)를 설정합니다.
ALTER TABLE orders FORCE ROW LEVEL SECURITY;
이제 슈퍼유저도 RLS 정책을 따라야 합니다.
RLS 활성화 후 발생할 수 있는 오류 및 해결책
"new row violates row-level security policy" 오류
원인: INSERT
를 위한 WITH CHECK
정책이 없음.
해결 방법: WITH CHECK
정책을 추가해야 합니다.
CREATE POLICY allow_insert_orders
ON orders
FOR INSERT
WITH CHECK (customer_id = current_user::text);
"permission denied for relation" 오류
원인: 테이블에 대한 SELECT
권한이 없음.
해결 방법: GRANT SELECT
및 RLS 정책 추가.
GRANT SELECT ON orders TO public;
CREATE POLICY allow_read_orders
ON orders
FOR SELECT
USING (true);
슈퍼유저 우회 불가 문제
원인: FORCE ROW LEVEL SECURITY
가 활성화됨.
해결 방법: FORCE ROW LEVEL SECURITY
해제.
ALTER TABLE orders NO FORCE ROW LEVEL SECURITY;
실제 서비스 적용 사례
멀티 테넌트 환경에서 RLS 활용
각 고객(tenant)이 자신의 데이터만 볼 수 있도록 tenant_id
를 기준으로 설정.
CREATE POLICY tenant_isolation
ON users
FOR SELECT, UPDATE, DELETE
USING (tenant_id = current_setting('app.current_tenant')::uuid);
current_setting('app.current_tenant')
을 사용하면 동적으로 테넌트 ID를 관리할 수 있음.
RLS 주요 기능 요약
기능 | 설명 |
---|---|
ENABLE ROW LEVEL SECURITY |
RLS 활성화 |
DISABLE ROW LEVEL SECURITY |
RLS 비활성화 |
CREATE POLICY |
새로운 RLS 정책 추가 |
DROP POLICY |
기존 RLS 정책 삭제 |
FORCE ROW LEVEL SECURITY |
슈퍼유저도 강제 적용 |
최적의 RLS 활용 방법
- 테이블에 RLS 활성화 후 필요한 정책을 추가 (
SELECT
,INSERT
,UPDATE
,DELETE
). - 멀티 테넌트 환경에서는
current_setting()
을 활용하여 동적 RLS 적용. - 정책 적용 후 반드시
pg_policy
를 조회하여 정상적으로 등록되었는지 확인. - 슈퍼유저도 제한이 필요한 경우
FORCE ROW LEVEL SECURITY
설정.
PostgreSQL에서 Row-Level Security (RLS)가 활성화된 모든 테이블을 찾아 비활성화하는 SQL 스크립트는 다음과 같습니다.
전체 RLS 비활성화 쿼리
DO $$
DECLARE
r record;
BEGIN
FOR r IN
SELECT relname
FROM pg_class
WHERE relrowsecurity = true
LOOP
EXECUTE format('ALTER TABLE %I DISABLE ROW LEVEL SECURITY;', r.relname);
END LOOP;
END $$;
pg_class
에서relrowsecurity = true
인 테이블을 조회 → RLS가 활성화된 테이블 목록을 가져옴.FOR
루프를 사용하여 각 테이블에 대해ALTER TABLE <table_name> DISABLE ROW LEVEL SECURITY;
실행.EXECUTE format(...)
을 이용해 동적으로 SQL을 실행하여 각 테이블의 RLS를 비활성화.
적용 후 확인 방법
SELECT relname, relrowsecurity
FROM pg_class
WHERE relrowsecurity = true;
- 실행 후 결과가 없으면 모든 테이블에서 RLS가 비활성화됨을 의미합니다.
⚠ 주의 사항
- 슈퍼유저 권한이 필요합니다. (
ALTER TABLE
권한이 필요) - 테이블 이름에 대한 충돌 방지를 위해
format('%I', table_name)
을 사용 (SQL 인젝션 방지). - 멀티 테넌트 환경에서는 보안 정책이 변경될 수 있으므로 주의해서 실행.
위 쿼리를 실행하면 모든 테이블에서 RLS가 비활성화됩니다.
댓글