Supabase RLS
📌 Row Level Security - RLS
Supabase의 RLS는 "Row-Level Security"의 약자.
- 데이터베이스 테이블의 행에 대한 유효성 검사를 관리하는 기능, 특히 권한에 대한 검사.
- 특정 사용자가 특정 행에 읽기, 쓰기 가능한지 체크하고 보호한다
행 수준의 보안 장점
- 블로그 포스팅이라는 테이블이 있다라고 가정 해보자.
- 포스팅이라는 하나의 테이블에는 사실 다른 사람들의 포스팅도 있다.
- 보통 서버에서 내 포스팅과 다른 사람의 포스팅이 섞이지 않도록 권한체크를 한다.
- 근데 이러한 기능을 DB Level에서 제어하는 것.!
- *권한에 대해서 Application Code가 아닌 DDL로 정의가 된다.
RLS는 테이블 단위로 적용시킬 수 있다.
- RLS enalbe 되면 RLS 정책을 만들어야 테이블을 조회할 수 있다.
"누구나 그 테이블을 읽을 수 있어 라는 정책" 을 만들어야 한다.
- *참고
- anon : 누구나
- authenticated : 인증된 사용자만
- service_role : 어드민 (= RLS PASS권)
RLS 로 해결하는 API 취약점
BOLA - Broken Object Level Authorization.
- 접근 권한이 없는 데이터에 접근을 하는 경우이다.
- 예를 들어, A 사용자는 자신의 정보만 볼 수 있어야 하는 데, 같은 권한 수준을 가진 B 사용자의 정보까지 볼 수 있는 경우를 말한다.
BFLA - Broken Function Level Authorization
- BOLA가 Access - 데이터 접근에 대한 문제라면 BFLA는 Action - 작업 수행에 대한 문제이다. 즉, 권한이 없는 작업을 수행하는 것이다.
- postgreSQL의 RLS 기능을 이용해서 BOLA, BFLA 예방할 수 있다.
ref : 11개 API 취약점
RLS 문법
DB에 트랜잭션이 들어가기 전후로 RLS이 적용된다.
3단계로 생각하면 좋다.
1.준비
- using expression 라는 문법구절을 이용한다.
- 위 조건에 맞는 데이터만 준비된다.
- *처음에 RLS를 잘못설정하여 데이터조차 조회가 안되는 실수를 범한다.
2.트랜잭션
- select, insert, update, delete 연산을 수행한다.
3.재확인
- with check expression 라는 문법구절을 이용한다.
- 위 조건검사에 실패하면 오류가 발생하며, 트랜잭션 롤백된다.
📌 RLS - SELECT POLICY
About Role
- anon : 로그인 안한 유저
- authenticated : 로그인 한 유저
- public : anon + authenticated
- owner : 데이터 행 소유자, auth.uid() = user_id
- public | authenticated | owner 3단계로 권한을 좁힌다고 생각하자.
*모든 roles : https://supabase.com/docs/guides/database/postgres/roles#supabase-roles
USING expression
- USING expression은 where절로 변환된다고 생각하면 된다.
- 즉, SELECT, UPDATE, DELETE 연산을 하기 전에 해당 Where 절이 먼저 실행된다.
- *트랜젝션 사전에 실행되는 것이다.
eg) SELECT에 auth.uid() = user_id을 걸면
- 로그인 한 사용자의 Row만 보인다.
설명
- USING expression은 행 수준 보안이 활성화된 경우 테이블을 참조하는 쿼리에 추가됩니다.
- 표현식이 true를 반환하는 행이 표시됩니다. 식이 false 또는 null을 반환하는 모든 행은 사용자에게 표시되지 않으며(SELECT에서) 수정할 수 없습니다(UPDATE 또는 DELETE에서).
- 이러한 행은 자동으로 표시되지 않으며 오류가 보고되지 않습니다.
--- 누구나 조회가 가능한 정책
CREATE POLICY "Enable select for authenticated users only" ON "public"."todos"
AS PERMISSIVE FOR SELECT
TO public -- public | anon | authenticated
USING (true)
-- 로그인 한 사용자만 조회가 가능한 정책
CREATE POLICY "Enable select for authenticated users only" ON "public"."todos"
AS PERMISSIVE FOR SELECT
TO authenticated
USING (true)
-- 로그인 했으며, 행의 소유권자만 조회가 가능함
CREATE POLICY "Enable select for users based on user_id" ON "public"."todos"
AS PERMISSIVE FOR SELECT
TO public
USING (auth.uid() = user_id)
-- * auth.uid() : 현재 인증된 사용자의 고유 ID
-- * user_id : 검사할 행의 user_id 값
-- 참고) using의 내부적인 변환 과정
-- https://supabase.com/docs/guides/auth/row-level-security#policies
create policy "Individuals can view their own todos."
on todos for select
using ( auth.uid() = user_id );
-- RLS(using 구문)이 적용된 select sql은 아래처럼 변환됩니다.
select * from todos
where auth.uid() = todos.user_id; -- RLS Policy is implicitly added.
📌 RLS - INSERT, UPDATE, DELETE POLICY
WITH CHECK expression
- INSERT, UPDATE 연산을 하고 난 후에 체크할 부분
설명
- 이 식은 행 수준 보안이 활성화된 경우 테이블에 대한 INSERT 및 UPDATE 쿼리에 사용됩니다.
- 표현식이 true로 평가되는 행만 허용됩니다. 삽입된 레코드나 업데이트로 인해 생성된 레코드에 대해 표현식이 false 또는 null로 평가되면 오류가 발생합니다.
- 이 표현식은 원래 내용이 아닌 행의 제안된 새 내용에 대해 평가됩니다.
CHECK Expression 은 사후 처리 검증을 시도하고 오류라면 롤백한다.
- todos 테이블에 대해서 insert : authenticated (로그인 한 사용자만 가능)
- todos 테이블에 대해서 update : owner (만든 사람만 가능)
- todos 테이블에 대해서 delete : owner (만든 사람만 가능)
-- INSERT는 사후 검증만 가능
CREATE POLICY "Enable insert for authenticated users only" ON "public"."todos"
AS PERMISSIVE FOR INSERT
TO authenticated
WITH CHECK (true) -- WITH CHECK expression
-- UPDATE는 더블체크
CREATE POLICY "Enable update for users based on user_id" ON "public"."todos"
AS PERMISSIVE FOR UPDATE
TO public
USING (auth.uid() = user_id) -- USING expression
WITH CHECK (auth.uid() = user_id) -- WITH CHECK expression
-- DELETE는 사전 검증만 가능
CREATE POLICY "Enable delete for users based on user_id" ON "public"."todos"
AS PERMISSIVE FOR DELETE
TO public
USING (auth.uid() = user_id) -- 로그인한 uid 랑 이 테이블의 user_id가 같은지 체크한다.