Replaces the original APAC-vs-all 2-user demo (vpduser_a/b on KR_ANALYSTS/GLOBAL_ADMINS groups) with a 2x2 source-access matrix: vpduser_my -> MY_ONLY group -> MySQL view only vpduser_pg -> PG_ONLY group -> Postgres view only vpduser_both -> BOTH_SOURCES group -> both views vpduser_none -> (no group) -> nothing (default deny) Why: source-level segmentation is the more common production permission story than region-level filtering. Region filtering remains available as an opt-in variant via commented UPDATE in sql/adb/03_seed.sql. Key changes: - 03_seed.sql, 07_end_users.sql, 00_cleanup.sql, .env.example, run.sh updated for the new 4-user model. All 4 users get identical view GRANTs; the only differentiator is the permission table (proves the model is "data-driven, not GRANT-driven"). - 08-11 split into one file per user: my (+ 5 bypass attempts), pg, both, none (default-deny verification). - 12_tests_admin_audit.sql uses LEFT JOIN so vpduser_none shows up as NULL permissions, and filters by object_owner=USER to exclude cross-schema policies. - Removed inline "-- comment" after ";" lines in 03_seed.sql: SQL*Plus silently skipped the inserts (documented gotcha). - README.md + docs/01,02 updated for the 4-user matrix. docs/03 detailed guide keeps the region-filter example but now has a preface explaining it's a variant of the default 4-user model. - docs/04: db_type='mysql_community' note added (RDS MySQL). E2E verified: PG=0/MY=17, PG=12/MY=0, PG=12/MY=17, PG=0/MY=0 plus all 5 bypass attempts blocked. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
48 KiB
VPD 기반 권한 관리 POC — 상세 설명서
이 문서의 대상 독자: 데이터베이스 보안이나 Oracle 기술에 익숙하지 않은 분. 용어가 나올 때마다 풀어서 설명하고, 왜 그 장치가 필요한지를 함께 적습니다.
시나리오 안내: 현재
./run.sh가 자동으로 깔아주는 기본 시나리오는 4명의 엔드유저 (vpduser_my/pg/both/none) 가 각각 다른 소스에 접근 가능한 소스 단위 매트릭스 입니다 (README 참고). 본 상세 가이드는 그 위에 얹을 수 있는 행 단위 region 필터링 변형(KR_ANALYSTS → APAC,GLOBAL_ADMINS → '*') 을 예시로 사용합니다 — VPD 메커니즘 자체는 동일하므로 개념 이해에는 차이가 없습니다. region 필터를 실제로 켜려면sql/adb/03_seed.sql하단의 주석을 해제하세요.
목차
- 무엇을 만들었고, 왜 만들었나
- 핵심 개념 풀어보기
- 전체 구조도
- 구성 요소별 설명
- 실제 동작 결과 (테스트 출력)
- “정말로 못 우회하나?” — 보안 보장 7 계층
- 추가 강화 옵션 (운영 단계용)
- 현재 POC의 한계와 운영 시 고려사항
- 디렉터리·파일 구조
- 실행 방법
- 데이터 카탈로그 인프라로서의 가능성 — Databricks Unity Catalog 와 비교
1. 무엇을 만들었고, 왜 만들었나
1-1. 풀고 싶은 문제
회사에는 여러 개의 데이터베이스가 있습니다. 어떤 데이터는 Oracle에, 어떤 데이터는 MySQL에, 어떤 데이터는 PostgreSQL에 들어 있어요. 분석가, 운영자, 외부 협력사 사람들이 각자 필요한 데이터만 볼 수 있어야 하는데, 각 데이터베이스마다 권한을 따로 설정하면 다음과 같은 문제가 생깁니다.
- 관리가 흩어진다: “이 사람한테 어디까지 보여주고 있더라?” 를 추적하기 어렵습니다.
- 일관성이 깨진다: MySQL에는 권한을 줬는데 Postgres에는 못 줬더라, 같은 실수가 흔합니다.
- 변경이 느리다: 한 사람의 권한을 바꾸려면 여러 시스템을 동시에 손봐야 합니다.
1-2. 해결 아이디어
이 POC의 핵심 아이디어는 “하나의 관문(Oracle ADB) 을 둬서 모든 사용자 질의(SELECT 문)를 거기로만 받게 하고, 그 관문 안에서 ‘이 사용자에게 어디까지 보여줄지’를 자동으로 결정하자”입니다.
- 관문: Oracle Autonomous Database (이하 ADB). 우리가 정책을 적용할 단 한 곳.
- 외부 데이터들: ADB가 DB Link 라는 통로로 MySQL/Postgres에 연결되어 있어, 마치 “ADB 안의 테이블”처럼 조회할 수 있습니다.
- 자동 결정: 사용자가
SELECT * FROM 고객테이블이라고만 쓰면, Oracle이 알아서 “이 사용자가 KR 분석가니까 region='APAC'인 행만 보여줘야지” 하고WHERE region='APAC'을 자동으로 붙여서 실행합니다. 이걸 VPD (Virtual Private Database) 라고 합니다.
1-3. 한 줄 요약
사용자가 단순한 SELECT 한 줄을 던져도, Oracle이 그 사람의 신분증을 보고 알아서 “보여줄 수 있는 행만” 골라줍니다. 사용자는 자기가 필터 받았다는 것조차 모를 수 있습니다.
2. 핵심 개념 풀어보기
2-1. VPD (Virtual Private Database)란?
“가상 사설 데이터베이스”라는 뜻인데, 이름이 좀 어렵게 들리지만 개념은 단순합니다.
비유: 회사 도서관에 책이 1만 권 있다고 합시다. 사람마다 열람 가능한 책이 다릅니다.
- 방식 A (전통): 사람마다 다른 도서관 건물을 만든다. (= 데이터를 복제) → 비싸고, 동기화 안 됨.
- 방식 B (VPD): 도서관은 하나. 그런데 들어오는 사람의 신분증을 보고 사서가 자동으로 그 사람이 못 보는 책장 앞에 가림막을 친다. 그 사람은 1만 권 중 자기에게 허용된 책만 보입니다.
VPD는 “방식 B”의 사서 역할을 Oracle이 자동으로 해 주는 기능입니다.
2-2. DB Link란?
ADB 안에 앉아서 SELECT * FROM customers@RDS_LINK 라고 쓰면, ADB가 알아서 AWS RDS의 MySQL까지 다녀와서 결과를 보여줍니다. 즉, “다른 DB로 가는 지름길” 같은 것입니다. 사용자 입장에선 외부 DB가 어디에 있든 ADB 안의 테이블처럼 보입니다.
이 POC의 ADB에는 두 개의 DB Link가 이미 만들어져 있습니다.
| DB Link 이름 | 가리키는 곳 |
|---|---|
RDS_LINK |
AWS RDS MySQL (ecommerce_poc 스키마) |
RDS_POSTGRES_LINK |
AWS RDS PostgreSQL (public 스키마) |
2-3. Application Context (애플리케이션 컨텍스트)란?
사용자가 ADB에 로그인하면 그 세션(연결)이 살아 있는 동안 “이 사람은 누구다”, “어디까지 볼 수 있다” 같은 정보를 세션 메모리에 저장해 둘 수 있습니다. 이걸 컨텍스트라고 부릅니다.
비유: 도서관에 들어올 때 받는 “오늘 출입증 + 열람권”. 출입증을 받은 사람은 도서관 안에서 굳이 매번 신분증을 꺼내지 않아도 됩니다. 사서(=Oracle)가 그 사람의 출입증을 자동으로 들여다봅니다.
POC에서는 이 컨텍스트를 VPD_CTX 라는 이름으로 만들어서, 로그인할 때 자동으로 “이 사용자가 볼 수 있는 region 목록”을 채워 둡니다.
2-4. Secure Application Context (보안 컨텍스트)
그냥 컨텍스트는 누구나 자기 마음대로 “나는 관리자야!”라고 적어 넣을 수 있어서 위험합니다. 그래서 Oracle은 “이 컨텍스트는 OO이라는 패키지(코드 묶음) 안에서만 설정될 수 있다” 고 못 박을 수 있습니다.
POC에서는:
CREATE OR REPLACE CONTEXT vpd_ctx USING ctx_pkg;
이 한 줄이 핵심입니다. vpd_ctx 컨텍스트는 오직 ctx_pkg 라는 패키지 안의 코드만이 값을 채울 수 있습니다. 사용자가 직접 DBMS_SESSION.SET_CONTEXT('VPD_CTX','...') 를 호출하면 ORA-01031: insufficient privileges 로 거절당합니다.
2-5. LOGON 트리거란?
“사용자가 데이터베이스에 로그인하는 순간, 자동으로 실행되는 작은 프로그램”입니다. 사용자가 뭘 하기 전에 시스템이 먼저 “환영합니다, 당신의 권한을 세팅해 둘게요” 라고 준비하는 역할이죠.
POC에서는 VPDUSER_A 또는 VPDUSER_B가 ADB에 로그인하는 순간 트리거가 발동되어 ctx_pkg.init 을 부르고, 그 결과로 vpd_ctx 컨텍스트에 그 사용자의 권한이 채워집니다. 사용자가 막을 수 없습니다. 트리거를 끄거나 우회할 권한 자체를 주지 않았기 때문입니다.
3. 전체 구조도
┌──────────────────────────────────────────────────────────────────────────┐
│ ADB (Oracle Autonomous DB) │
│ │
│ ┌──────────────┐ │
│ │ VPDUSER_A │ ① 로그인 │
│ │ VPDUSER_B │ ─────────► ┌─────────────────────────┐ │
│ └──────────────┘ │ LOGON 트리거 │ │
│ │ │ = vpd_logon_trg │ │
│ │ └──────────┬──────────────┘ │
│ │ │ ② ctx_pkg.init 호출 │
│ │ ▼ │
│ │ ┌─────────────────────────┐ │
│ │ │ ctx_pkg.init │ │
│ │ │ - permission 테이블 조회 │ │
│ │ │ - SET_CONTEXT │ │
│ │ └──────────┬──────────────┘ │
│ │ │ ③ 세션 메모리에 권한 저장 │
│ │ ▼ │
│ │ ┌─────────────────────────┐ │
│ │ │ VPD_CTX (보안 컨텍스트) │ │
│ │ │ USER_ID=1 │ │
│ │ │ V_CUSTOMERS_PG='APAC' │ │
│ │ │ V_CUSTOMERS_MY='APAC' │ │
│ │ └─────────────────────────┘ │
│ │ │
│ │ ④ SELECT * FROM v_customers_pg │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ v_customers_pg (로컬 뷰) │ ← VPD 정책 부착 │
│ └──────┬──────────────────────┘ ⑤ vpd_region_filter 함수 호출 │
│ │ │ │
│ │ ▼ "region IN ('APAC')" 반환 │
│ │ │
│ │ ⑥ 사용자의 질의에 WHERE 절 자동 결합: │
│ │ SELECT * FROM "public"."customers"@RDS_POSTGRES_LINK │
│ │ WHERE region IN ('APAC') │
│ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ DB Link: RDS_POSTGRES_LINK / RDS_LINK │ │
│ └────────────────┬───────────────────────────────┘ │
└────────────────────┼───────────────────────────────────────────────────────┘
│
▼
┌───────────────────────┐ ┌───────────────────────┐
│ AWS RDS PostgreSQL │ │ AWS RDS MySQL │
│ public.customers │ │ ecommerce_poc.customers│
└───────────────────────┘ └───────────────────────┘
4. 구성 요소별 설명
POC는 9개의 SQL 파일로 이루어져 있고, 각각 한 가지 역할을 합니다.
4-1. sql/00_cleanup.sql — 초기화
POC를 몇 번이고 새로 깔 수 있도록, 이전에 만든 것을 모두 지웁니다. 처음 실행할 땐 “없는데 지우라고?” 하는 에러가 나도 무시하도록 EXCEPTION WHEN OTHERS THEN NULL 처리를 합니다.
4-2. sql/01_perm_tables.sql — 권한 모델 (정책 평면)
“누가, 어느 그룹에 속하고, 그 그룹은 어느 데이터 소스의 어느 객체에서 어떤 행을 볼 수 있는가”를 저장하는 테이블 6개를 만듭니다.
app_customer ──┐
├── 회사 단위 격리 (멀티테넌트 대비)
app_user ──┤
│ ↑ 각 사용자는 1개 회사에 속함
app_group ──┤
│ ↑ 그룹도 회사 단위로 관리
user_group ──┘ ↑ 사용자 ↔ 그룹 N:M
db_source ── 외부 데이터 소스 정의 (어느 DB Link인지)
permission ── 핵심: 그룹 × 소스 × 객체 → 허용 region 목록
permission.allowed_regions 컬럼은 'APAC' 처럼 콤마로 구분된 값이거나 '*' 입니다. '*' 는 “모든 region 허용 = 필터 없음”을 뜻합니다.
4-3. sql/02_seed.sql — 시연용 데이터 주입
VPDUSER_A → KR_ANALYSTS 그룹 → APAC 만 조회 가능
VPDUSER_B → GLOBAL_ADMINS 그룹 → 전 region 조회 가능 (*)
이 두 사용자를 가지고 “같은 SQL 한 줄을 던졌을 때 결과가 다르다”는 것을 보일 겁니다.
4-4. sql/03_secure_ctx.sql — 보안 컨텍스트와 패키지
세 가지가 한 번에 만들어집니다.
ctx_pkg패키지의 명세:init이라는 프로시저가 있다고 선언.ctx_pkg패키지의 본문: 실제 로직.- 지금 로그인한 Oracle 사용자 이름을 알아낸다 (
SYS_CONTEXT('USERENV','SESSION_USER')). - 그 사람을
app_user테이블에서 찾는다. user_group→permission을 따라가서 “이 사람이 어느 객체에 어떤 region 까지 허용됐는지” 조회.- 결과를
VPD_CTX컨텍스트 안에 객체별 속성으로 저장. (예:VPD_CTX.V_CUSTOMERS_PG = 'APAC')
- 지금 로그인한 Oracle 사용자 이름을 알아낸다 (
vpd_ctx보안 컨텍스트 자체:USING ctx_pkg라고 못 박아서, 다른 곳에서는 절대 이 값을 쓸 수 없게 막는다.
4-5. sql/04_views.sql — 외부 테이블에 대한 로컬 뷰
ADB 안에서 사용자가 보게 될 “테이블처럼 생긴 것”을 두 개 만듭니다. 둘 다 그저 외부 DB의 customers 테이블을 들여다보는 창문(view)일 뿐입니다.
v_customers_pg→ PostgreSQL의public.customersv_customers_my→ MySQL의ecommerce_poc.customers
⚠️ 중요: 사용자에게는 이 뷰만 보여줍니다. 뒤에 있는 customers@RDS_POSTGRES_LINK 같은 원본 참조는 절대 직접 접근하게 두면 안 됩니다. 그래야 정책을 우회할 수 없습니다.
4-6. sql/05_policy.sql — VPD 정책 함수와 부착
이게 마법의 핵심입니다.
- 정책 함수
vpd_region_filter: 사용자의 컨텍스트에서 허용 region 을 읽고,region IN ('APAC')같은 문자열을 돌려준다.- 만약 컨텍스트에 아무것도 없으면 →
1=0을 돌려준다. (= “행 0개” = fail-closed 안전장치) - 만약
'*'가 있으면 →NULL을 돌려준다. (= 필터 없음 = 전부 보임)
- 만약 컨텍스트에 아무것도 없으면 →
DBMS_RLS.ADD_POLICY로 위 함수를v_customers_pg,v_customers_my두 뷰에 “SELECT 문에 자동 결합되게” 부착.
이후 사용자가 SELECT * FROM v_customers_pg 라고 쓰면 Oracle이 내부적으로 SELECT * FROM v_customers_pg WHERE region IN ('APAC') 로 바꿔서 실행합니다. 사용자는 자기 쿼리에 WHERE이 추가됐다는 사실조차 모릅니다.
4-7. sql/06_end_users.sql — 일반 사용자 계정과 LOGON 트리거
VPDUSER_A, VPDUSER_B 라는 일반 사용자 두 명을 만듭니다. 이 두 계정에 주는 권한은 딱 4 가지입니다.
✅ CREATE SESSION (로그인할 권리)
✅ SELECT on v_customers_pg
✅ SELECT on v_customers_my
✅ EXECUTE on ctx_pkg (트리거가 부를 수 있도록)
그 외에는 아무것도 안 줍니다. 특히:
❌ permission, app_user 등 권한 테이블 — 못 봄
❌ 원본 customers@DB_LINK — 못 봄
❌ DBMS_RLS — 정책을 끄거나 바꿀 권한 없음
❌ EXEMPT ACCESS POLICY — VPD를 무시할 시스템 권한 없음
❌ CREATE TABLE / CREATE MATERIALIZED VIEW — 필터된 결과를 스냅샷으로 빼낼 수 없음
❌ DBA 계열 ROLE 일체 없음
마지막으로 LOGON 트리거 를 만듭니다.
CREATE OR REPLACE TRIGGER vpd_logon_trg
AFTER LOGON ON DATABASE
BEGIN
IF SYS_CONTEXT('USERENV','SESSION_USER') IN ('VPDUSER_A','VPDUSER_B') THEN
admin.ctx_pkg.init;
END IF;
END;
이 트리거는 데이터베이스 전체 단위로 “누가 로그인하든” 발동되고, 그 사람이 우리가 관리하는 사용자면 컨텍스트를 자동으로 채웁니다.
4-8. sql/07_tests_user_a.sql, 08_tests_user_b.sql
각 사용자가 직접 쿼리해서, 정말 자기에게 허용된 행만 보이는지 확인. 그리고 5가지 우회 시도 가 전부 실패하는지 확인.
4-9. sql/09_tests_admin_audit.sql
관리자(ADMIN) 입장에서 “지금 어떤 정책들이 어느 뷰에 부착돼 있나?”, “누구한테 어떤 권한이 주어졌나?” 를 한눈에 보기 위한 감사용 스크립트. 다음 네 가지를 출력합니다.
- Attached VPD policies —
DBA_POLICIES에서V_CUSTOMERS_PG,V_CUSTOMERS_MY에 붙은 정책(CUSTOMERS_PG_POLICY,CUSTOMERS_MY_POLICY) 과 활성화 상태. - Attached Data Redaction policies —
REDACTION_POLICIES에서PII_REDACT_PG,PII_REDACT_MY의 게이팅 식(SYS_CONTEXT('VPD_CTX','<뷰명>') IS NULL OR != '*') 을 그대로 확인. - Redacted columns —
REDACTION_COLUMNS에서 어느 컬럼(EMAIL,FULL_NAME)이 어떤 정규식(^(.)(.*)(@.*)$→\1****\3등)으로 마스킹되는지 표시. - Permission summary —
app_user / user_group / app_group / permission / db_source조인으로 “누가 어느 그룹에 속해 어느 소스의 어느 뷰에서 어느 region 까지 보는지” 가 한눈에.
즉 이 한 스크립트가 행 단위(VPD) + 컬럼 단위(Redaction) + 권한 모델(permission 테이블) 세 계층을 모두 들여다봅니다.
5. 실제 동작 결과 (테스트 출력)
5-1. VPDUSER_A — KR 분석가, APAC만 허용
SQL> SELECT USER, SYS_CONTEXT('VPD_CTX','V_CUSTOMERS_PG') AS regions FROM dual;
USER REGIONS
---------- -------
VPDUSER_A APAC
SQL> SELECT customer_id, full_name, email, region FROM admin.v_customers_pg ORDER BY customer_id;
CUSTOMER_ID FULL_NAME EMAIL REGION
----------- ------------ --------------------- ------
3 A**** a****@example.com APAC
5 C**** c****@example.com APAC
이 한 결과에 두 가지 보호 계층 이 동시에 보입니다.
- 행 단위 (VPD) — 원본은 5건이지만 2건만 보입니다.
region IN ('NA','EMEA')행은 VPDUSER_A 의 세션에서는 “존재 자체를 모릅니다.”WHERE region='NA'같은 조건을 걸어도 0건이 나옵니다. - 컬럼 단위 (Data Redaction) — 보이는 2건의
full_name과email도 원본이 아닙니다.Alice Kim이A****,alice.kim@example.com이a****@example.com으로 마스킹돼서 내려옵니다. 정규식 마스킹은 ADB 엔진이 컬럼 표시 직전에 적용하므로, 클라이언트 어디서도 원본 값을 받아볼 수 없습니다.
5-2. VPDUSER_B — 글로벌 관리자, 전부 허용
SQL> SELECT customer_id, full_name, email, region FROM admin.v_customers_pg ORDER BY customer_id;
CUSTOMER_ID FULL_NAME EMAIL REGION
----------- -------------- --------------------------- ------
1 John Doe john.doe@example.com NA
2 Jane Smith jane.smith@example.com EMEA
3 Alice Kim alice.kim@example.com APAC
4 Bob Johnson bob.j@example.com NA
5 Charlie Lee charlie.lee@example.com APAC
같은 SQL, 다른 결과.
- 행 5건 다 보임 — VPDUSER_B 는
allowed_regions='*'이므로 VPD 가 행 필터를 붙이지 않습니다. - 컬럼도 원본 그대로 — 같은
*권한이 Data Redaction 게이팅 식 (SYS_CONTEXT('VPD_CTX','V_CUSTOMERS_PG') != '*') 을 통과하지 못하므로 마스킹이 작동하지 않고 원본을 그대로 반환합니다.
같은 한 줄의 SQL 이 사용자 신원만 다를 뿐, 보이는 행 수도 다르고 PII 값도 다릅니다. 이것이 VPD + Data Redaction 이 ADB 엔진 안에서 동작한 증거입니다.
5-3. 우회 시도 — 전부 실패
VPDUSER_A 가 시도해 본 5 가지 우회법은 모두 막혔습니다.
| 시도 | 결과 |
|---|---|
① 원본 외부 테이블 직접 조회 (SELECT FROM ...@RDS_POSTGRES_LINK) |
❌ ORA-02019 (DB Link 자체를 모름) |
② 컨텍스트 위조 (DBMS_SESSION.SET_CONTEXT(...) 직접 호출) |
❌ ORA-01031: insufficient privileges |
③ VPD 정책 삭제 (DBMS_RLS.DROP_POLICY) |
❌ PLS-00201: DBMS_RLS must be declared (실행 권한 없음) |
④ 권한 테이블 조회 (SELECT FROM admin.permission) |
❌ ORA-00942: table or view does not exist |
⑤ 사용자 테이블 조회 (SELECT FROM admin.app_user) |
❌ ORA-00942 |
6. “정말로 못 우회하나?” — 보안 보장 7 계층
VPD는 “Oracle 안에서” 강력하지만, “Oracle 밖” 까지 보장해 주진 않습니다. 다음 7 가지 장치가 함께 있어야 빈틈없이 막힙니다. 각 항목은 “만약 이걸 안 해두면 어떻게 뚫리나” 의 시나리오와 함께 설명합니다.
6-1. LOGON 트리거 — 항상 컨텍스트가 세팅된다
뚫리는 시나리오 (장치가 없다면): 사용자가 컨텍스트를 안 채우고 그냥 쿼리. 정책 함수가 NULL 을 반환해서 “필터 없음” 으로 해석되면 전부 보임.
막는 방법: 로그인하는 그 순간 DB 엔진이 트리거를 강제로 실행. 사용자가 SQL을 한 줄도 치기 전에 컨텍스트가 채워진다. 클라이언트가 SQL*Plus든 JDBC든 ORDS든 상관없이 적용.
6-2. Fail-Closed 정책 — 컨텍스트 없으면 “0행”
뚫리는 시나리오 (장치가 없다면): 어떤 이유로 트리거가 실패하면 컨텍스트가 비어 있고, 정책이 “필터 없음 = 전부” 로 해석.
막는 방법: 정책 함수가 컨텍스트에 권한이 없으면 무조건 1=0 을 반환해서 행 0개. 사고 시 데이터가 새 나가지 않고 그냥 “안 보임” 으로 안전하게 실패한다.
6-3. Secure Application Context — 위조 불가
뚫리는 시나리오 (장치가 없다면): 사용자가 DBMS_SESSION.SET_CONTEXT('VPD_CTX','V_CUSTOMERS_PG','*') 한 줄로 자기 권한을 무한대로 만든다.
막는 방법: CREATE CONTEXT vpd_ctx USING ctx_pkg. 이 컨텍스트는 오직 ctx_pkg 패키지 안에서 호출되는 SET_CONTEXT만 받는다. 사용자가 직접 호출하면 ORA-01031 로 거절.
6-4. 최소 권한 원칙 — 뷰 외에는 못 만진다
뚫리는 시나리오 (장치가 없다면): 사용자가 권한 테이블을 직접 UPDATE 해서 자기 그룹의 allowed_regions 를 * 로 바꿔버린다.
막는 방법: 사용자에게 테이블 조회·수정·정책 변경 권한을 일체 주지 않는다. 오직 뷰의 SELECT 권한과 ctx_pkg 실행 권한만.
6-5. 외부 데이터 직접 접근 차단
뚫리는 시나리오 (장치가 없다면): 사용자가 SELECT * FROM customers@RDS_LINK 를 직접 쳐서 VPD가 안 걸린 원본을 들여다본다.
막는 방법: DB Link 는 ADMIN 의 PRIVATE DB Link. 다른 사용자에게 사용 권한을 주지 않으면 그 이름 자체를 알 수 없고 (ORA-02019: connection description not found) 호출도 불가.
6-6. EXEMPT ACCESS POLICY 권한 차단
뚫리는 시나리오 (장치가 없다면): 어떤 사용자에게 DBA 권한을 잘못 줘서 EXEMPT ACCESS POLICY 시스템 권한이 따라간다. 이 권한이 있으면 VPD가 통째로 무시된다.
막는 방법: 일반 사용자에겐 DBA, PDB_DBA 같은 묶음 권한을 절대 부여하지 않는다. POC에서는 CREATE SESSION 외에 ROLE 자체를 안 줬다.
6-7. 정책 조작 권한 차단
뚫리는 시나리오 (장치가 없다면): 사용자가 DBMS_RLS.DROP_POLICY('ADMIN','V_CUSTOMERS_PG','...') 을 호출해서 정책을 끈다.
막는 방법: DBMS_RLS 패키지에 대한 EXECUTE 권한을 주지 않는다. 시도하면 PLS-00201: identifier 'DBMS_RLS' must be declared 로 거절.
7. 추가 강화 옵션 (운영 단계용)
POC 에서는 아래 옵션을 문서화만 해 둡니다. 실제 운영 환경에서는 다음 중 필요한 것을 추가하시면 됩니다.
7-1. LOGON 트리거를 “Hard-Fail” 모드로 바꾸기
지금은 트리거 안에 EXCEPTION WHEN OTHERS THEN NULL 이 있어서, 컨텍스트 로딩이 실패해도 로그인 자체는 성공합니다 (대신 정책이 fail-closed 라 데이터는 안 보임).
운영 환경에선 “문제가 있으면 아예 로그인 자체를 거부” 하는 편이 모니터링상 명확 합니다. 다음과 같이 바꿉니다.
CREATE OR REPLACE TRIGGER vpd_logon_trg
AFTER LOGON ON DATABASE
BEGIN
IF SYS_CONTEXT('USERENV','SESSION_USER') IN ('VPDUSER_A','VPDUSER_B') THEN
admin.ctx_pkg.init;
-- 컨텍스트가 비어 있으면 로그인 거부
IF SYS_CONTEXT('VPD_CTX','USER_ID') IS NULL THEN
RAISE_APPLICATION_ERROR(-20001,
'VPD context could not be loaded. Login blocked for safety.');
END IF;
END IF;
END;
/
이러면 트리거가 실패하거나 컨텍스트가 비어 있을 때 사용자는 연결 자체가 안 됩니다. “행이 안 보인다”가 아니라 “들어올 수 없다” 가 되므로 보안 사고 감지가 빨라집니다.
7-2. Proxy Authentication (대리 인증)
웹 애플리케이션, ORDS, APEX 등에서 자주 만나는 함정은 다음과 같습니다.
앱 서버가 항상 ADMIN 계정으로 ADB에 접속한 뒤, “지금 로그인한 사용자는 김철수입니다” 같은 정보를 앱 코드 안에서만 들고 있는 경우. → ADB 입장에서 세션은 ADMIN 이므로 LOGON 트리거가 김철수 용으로 발동하지 않고, VPD가 김철수의 권한을 알 수가 없음. 모든 데이터가 다 보임.
이를 막으려면 Proxy Authentication 을 씁니다.
-- 미리 한 번:
ALTER USER vpduser_a GRANT CONNECT THROUGH app_user;
ALTER USER vpduser_b GRANT CONNECT THROUGH app_user;
이제 앱 서버는 app_user[vpduser_a]/password@db 같은 식으로 접속합니다.
- 앱 서버는 vpduser_a 의 비밀번호를 모르고도,
- ADB 안에서 세션의 정체는 vpduser_a 가 되며,
- LOGON 트리거가 vpduser_a 용으로 정상 발동하고,
- VPD가 정상 동작합니다.
ORDS, APEX, JDBC 풀, Python 앱 모두 이 패턴을 지원합니다.
7-3. Unified Audit — 누가 무엇을 시도했는지 기록
VPD 가 막아도, “누가 막혔는지” 를 기록해두면 침입 시도 감지나 사고 조사에 좋습니다. ADB는 기본적으로 Unified Audit이 켜져 있고, 다음 같은 조건으로 감사 정책을 추가할 수 있습니다.
CREATE AUDIT POLICY vpd_view_access
ACTIONS SELECT ON admin.v_customers_pg,
SELECT ON admin.v_customers_my;
AUDIT POLICY vpd_view_access;
이후 UNIFIED_AUDIT_TRAIL 뷰에서 누가 언제 어떤 뷰를 조회했는지 다 보입니다.
7-4. 권한 변경의 즉시 반영
지금 구조에서 permission 테이블을 바꾸면, 이미 로그인되어 있는 사용자의 세션 컨텍스트는 그대로 입니다. 다음 로그인부터 적용됩니다. 즉시 반영이 필요하면 두 가지 방법이 있습니다.
- 세션 강제 종료: ADMIN 이 변경 후 그 사용자 세션을
ALTER SYSTEM KILL SESSION으로 끊는다. 다음 로그인 때 새 권한 적용. - 정책 함수를 컨텍스트가 아닌 테이블 직접 조회로 작성: 매 쿼리마다
permission테이블을 읽으니 즉시 반영. 성능 비용 이 큼 (POC 에서 피한 이유).
7-5. Materialized View / 결과 캐시 (성능 최적화)
지금 POC 는 “일단 동작” 에 초점이 있어, DB Link 너머에서 데이터를 다 가져온 뒤 ADB 가 필터링합니다. 큰 테이블에서는 느립니다. 운영 단계에서 가능한 최적화 옵션:
- Materialized View: 외부 데이터를 주기적으로 ADB 안으로 복제하고 그 위에 VPD 적용. 사용자는 ADB 안의 사본만 조회 → DB Link 가 매번 안 일어남.
- Result Cache: 같은 쿼리 결과를 메모리에 캐싱.
- Predicate Pushdown: 정책이 만든 WHERE 절을 외부 DB 쪽으로 밀어 보내기. 이종 DB(MySQL/Postgres) 에선 제한적.
8. 현재 POC의 한계와 운영 시 고려사항
| 항목 | 현재 POC 상태 | 운영 시 권장 |
|---|---|---|
| 정책 평면 스키마 | ADMIN 한 곳에 다 있음 |
VPD_OWNER 같은 별도 스키마로 분리, ADMIN과 격리 |
| 패키지 권한 | AUTHID DEFINER |
그대로 (정책 함수도 DEFINER 가 맞음) |
| 권한 테이블 변경 즉시 반영 | 다음 로그인부터 적용 | 7-4 참조 |
| 성능 (DB Link) | Predicate pushdown 거의 안 됨 | 7-5 참조 |
| 사용자 비밀번호 관리 | 평문 비밀번호 | IAM 또는 Proxy Auth |
| 컨텍스트 위조 | 차단됨 (Secure Context) | 그대로 |
| 외부 DB 직접 접근 | DB Link 사용 권한 없어서 차단 | + 네트워크 단에서 RDS에 직접 접근 못 하도록 보안그룹 잠금 |
| 정책 조작 | 막혀 있음 | 그대로 |
| 감사 로깅 | 없음 | Unified Audit 추가 (7-3 참조) |
| LOGON 실패 정책 | Soft-fail (들어오긴 하나 데이터 0건) | Hard-fail (7-1 참조) |
| 정책 우회 위험성 검토 | 5 가지 시도 모두 차단 확인 | 신규 객체 추가할 때마다 동일 검증 절차 필요 |
9. 디렉터리·파일 구조
vpd-permission-poc/
├── .env ← ADB + RDS 연결 정보 (.gitignore 됨)
├── .env.example
├── README.md ← 클론-앤-고 가이드
├── run.sh ← 원클릭 오케스트레이터
├── scripts/lib/common.sh
├── sql/
│ ├── source/
│ │ ├── postgres_setup.sql ← 원격 PG: customers + 12 rows
│ │ └── mysql_setup.sql ← 원격 MySQL: customers + 12 rows
│ └── adb/
│ ├── 00_cleanup.sql ← 멱등 초기화
│ ├── 01_dblinks.sql ← DB Link + credential
│ ├── 02_perm_tables.sql ← 권한 매핑 6개 테이블
│ ├── 03_seed.sql ← 4-user 매트릭스 시드
│ ├── 04_secure_ctx.sql ← ctx_pkg + Secure Context
│ ├── 05_views.sql ← DB Link 통합 뷰
│ ├── 06_policy.sql ← VPD 정책 + 정책 함수
│ ├── 06a_redaction.sql ← Data Redaction (이메일/이름 마스킹)
│ ├── 07_end_users.sql ← 4 유저 + LOGON 트리거
│ ├── 08_tests_user_my.sql ← MY only + 우회 시도 5종
│ ├── 09_tests_user_pg.sql ← PG only
│ ├── 10_tests_user_both.sql ← both
│ ├── 11_tests_user_none.sql ← default deny 검증
│ └── 12_tests_admin_audit.sql
└── docs/
├── 01-quickstart.md
├── 02-architecture.md
└── 03-detailed-guide.md ← (이 문서)
10. 실행 방법
cd vpd-permission-poc
# 1) 전체 파이프라인 한 번에
./run.sh # = ./run.sh all
# 또는 단계별로:
./run.sh prereq # 도구/.env 검증
./run.sh source # 원격 PG + MySQL 에 customers 테이블/seed
./run.sh adb # ADB 측 cleanup → dblink → 권한/뷰/정책 → 4 유저
./run.sh tests # 4 명 (MY/PG/BOTH/NONE) 시점 테스트
./run.sh audit # ADMIN 시점 정책·권한 감사
./run.sh teardown # ADB 측 객체 + dblink 정리
사람의 눈으로 직접 확인하고 싶을 때
source .env
# 기본 시나리오 — vpduser_pg: PG 뷰는 다 보이고 MySQL 뷰는 0 rows
sqlplus "vpduser_pg/\"${VPDUSER_PG_PASSWORD}\"@$ADB_TNS"
SQL> SELECT COUNT(*) FROM admin.v_customers_pg; -- 12
SQL> SELECT COUNT(*) FROM admin.v_customers_my; -- 0
# vpduser_none — default deny
sqlplus "vpduser_none/\"${VPDUSER_NONE_PASSWORD}\"@$ADB_TNS"
SQL> SELECT COUNT(*) FROM admin.v_customers_pg; -- 0
SQL> SELECT COUNT(*) FROM admin.v_customers_my; -- 0
# region 필터 변형을 켰을 때 — vpduser_both 에게 APAC 만 허용한 경우
# (sql/adb/03_seed.sql 하단 UPDATE 주석 해제 후)
sqlplus "vpduser_both/\"${VPDUSER_BOTH_PASSWORD}\"@$ADB_TNS"
SQL> SELECT region, COUNT(*) FROM admin.v_customers_pg GROUP BY region;
-- 결과: APAC 만 보임
11. 데이터 카탈로그 인프라로서의 가능성 — Databricks Unity Catalog 와 비교
이 POC 를 단순히 “권한 관리” 가 아니라 데이터 카탈로그 플랫폼의 기반(인프라) 으로 확장할 수 있을까? 결론부터 말씀드리면:
“네, 가능합니다. 그리고 사실 ADB 자체에 데이터 카탈로그 기능이 이미 들어 있기 때문에, 우리가 만든 VPD 권한 계층과 결합하면 별도 제품 없이도 의미 있는 카탈로그 플랫폼이 됩니다.”
11-1. ADB 가 “기본 탑재” 로 제공하는 카탈로그 기능들
ADB 의 Database Actions / Data Studio 콘솔 안에는 별도 라이선스 없이 쓸 수 있는 데이터 카탈로그 도구들이 포함되어 있습니다.
| 기능 | 어디에서 제공되나 | 무엇을 해 주나 |
|---|---|---|
| Data Catalog (Data Studio 안의 도구) | Database Actions → Data Studio → Catalog | 객체(Entity) 등록, 비즈니스 모델, 용어집(Term Glossary), 기본 데이터 계보(lineage) |
| Sensitive Data Discovery (SDD) | Data Studio → Data Analysis | 스키마를 스캔해서 PII(개인정보) 컬럼을 자동 탐지·태깅 |
| Data Profiling | Data Studio → Data Insights | 컬럼 통계, 분포, NULL 비율, 이상치 등 |
| Business Glossary / Terms | Data Catalog 안 | 비즈니스 용어를 데이터 객체와 매핑 |
| Data Dictionary | ALL_TABLES, ALL_TAB_COLUMNS 등 |
모든 카탈로그 도구의 기반이 되는 메타데이터 |
| ORDS (REST 자동 노출) | Database Actions → REST | 객체에 REST 엔드포인트를 자동 부여 |
| APEX | Database Actions → APEX | 권한·메타데이터 위에 노코드 UI 구축 |
| Unified Audit | DB 엔진 내장 | 모든 접근·실패 시도 감사 로그 |
즉, “카탈로그” 라는 말이 의미하는 대부분의 기능(탐색·분류·민감정보 태깅·프로파일링·용어집·기본 계보·감사) 이 이미 ADB 안에 있습니다. 우리가 새로 만든 것은 그 위에서 “행 단위 권한을 자동으로 적용하는 집행 계층” 입니다.
11-2. Unity Catalog 가 제공하는 것 vs. 우리 ADB + VPD 가 제공하는 것
| 기능 | ADB 기본 + 우리 POC | Unity Catalog | 메모 |
|---|---|---|---|
| 단일 정책 평면 (중앙 집중 권한 관리) | ✅ (POC 가 추가) | ✅ | 동등 |
| Row-level Security (행 단위 권한) | ✅ (POC: VPD) | ✅ | 동등 |
| Column-level Masking (컬럼 단위 마스킹) | ✅ (POC: Oracle Data Redaction, email/full_name 마스킹) |
✅ | 동등 |
| 이종(Multi-source) DB 통합 | ✅ DB Link / External Tables | ✅ External Location | 동등 |
| 사용자 신원 기반 집행 | ✅ LOGON + Context | ✅ 컴퓨트 계층 | 동등 |
| 메타데이터 카탈로그 (테이블·소유자·태그) | ✅ Data Studio Catalog 내장 | ✅ | 동등 |
| 탐색 UI / 검색 | ✅ Database Actions 내장 | ✅ | 동등 |
| 비즈니스 용어집 (Glossary) | ✅ Data Catalog Terms 내장 | ✅ | 동등 |
| 민감정보 자동 분류 / 태깅 | ✅ Sensitive Data Discovery 내장 | ✅ | 동등 |
| 데이터 프로파일링 | ✅ Data Insights 내장 | ✅ | 동등 |
| 감사 로그 | ✅ Unified Audit 내장 | ✅ | 동등 |
| 기본 데이터 계보 (Lineage) | ⚠️ ADB 내부 한정 | ✅ 폭넓음 | 부분 동등 |
| 조직 간 데이터 공유 | ⚠️ Oracle Data Share 있으나 범위 좁음 | ✅ Delta Sharing | 약점 |
| SQL 이외 경로(예: Spark on S3) 의 통제 | ❌ ADB 를 거치지 않으면 무력 | ✅ Databricks 컴퓨트 거치면 통제됨 | 이게 가장 큰 차이점 |
11-3. 가장 중요한 차이점 — “어디서” 정책이 집행되는가
Unity Catalog 의 강점: 정책을 컴퓨트 계층(Spark, SQL Warehouse) 에서 강제합니다. 즉, Databricks 에서 돌아가는 Spark 작업이 S3 의 Parquet 파일을 직접 읽어도 그 작업이 Databricks 컴퓨트 위에서 실행되는 한 정책이 적용됩니다.
ADB+VPD 의 강점: 정책을 데이터베이스 내부(SQL parser) 에서 강제합니다. 어떤 클라이언트(BI 도구, JDBC, ORDS, sqlplus...)에서 들어오든, ADB 를 통해 들어오는 모든 쿼리는 무조건 막힙니다. “정책을 호출하지 않으면 그만” 같은 회피가 불가능합니다.
그렇다면 한계는? ADB 는 “ADB 를 거치지 않는 접근” 은 막을 수 없습니다. 누군가 Spark 작업을 짜서 RDS 에 직접 붙으면 ADB 에는 아무 흔적이 안 남습니다. 이 빈틈은 두 가지로 메웁니다.
- 아키텍처 규칙: “이 데이터 소스들에 대한 모든 접근은 ADB 를 통해야 한다” 를 조직 규칙으로 못 박는다.
- 네트워크 통제: RDS 인스턴스를 외부에서 직접 못 부르도록 보안그룹/VPC 단에서 잠근다. 오직 ADB 만이 RDS 까지 도달 가능하게.
기술적 보장이 아니라 설계 결정입니다. 충분히 달성 가능하지만 기술이 자동으로 막아주는 게 아니라는 점을 인지하셔야 합니다.
11-4. ADB+VPD 를 카탈로그 인프라로 쓸 때의 진짜 강점
- 집행이 수학적으로 보장된다. VPD 는 SQL 파서 단계에서 적용됩니다. 어떤 클라이언트도, 어떤 BI 도구도, 어떤 드라이버도 이 정책을 건너뛸 수 없습니다. 앱 코드가 “정책 함수 호출하는 걸 깜빡했어요” 같은 사고가 원천 차단됩니다.
- 데이터 이동이 필요 없다. Unity Catalog 의 진가는 데이터가 Databricks 의 Delta Lake 안에 있을 때 발휘됩니다. ADB+VPD 는 MySQL 은 MySQL 인 채로, Postgres 는 Postgres 인 채로 통합 거버넌스를 줍니다.
- 다종(polyglot) RDBMS 환경에 강하다. 이종 DB Link 와 Database Gateway 로 Oracle + MySQL + Postgres + SQL Server + Db2 등을 한 시야에서 묶을 수 있습니다.
- SQL 네이티브. Oracle Net 이나 JDBC 만 말할 줄 알면 어떤 BI 도구든 그대로 붙습니다. 별도 커넥터를 만들 필요가 없습니다.
- 규제 산업 (금융·보험·의료) 에 매우 적합합니다. “페타바이트 스케일” 보다 “감사 추적과 증명 가능한 집행” 이 더 중요한 영역에서 이 구조는 자연스럽습니다.
11-5. ADB+VPD 가 적합하지 않은 경우
- 레이크하우스 / 대용량 객체 스토리지 분석 (TB ~ PB 단위 스캔, ML 파이프라인 등). 이종 DB Link Gateway 는 Predicate Pushdown 이 제한적이라, 큰 결과를 다 끌어와 로컬에서 필터링하는 일이 생깁니다.
- 이미 Spark 중심으로 굴러가는 조직. 사용자가 굳이 ADB 를 거치지 않으려 들 가능성이 높습니다.
- 멀티 클라우드·멀티 계정 거버넌스가 이미 Unity / Lake Formation / Atlan 등으로 정착된 환경.
11-6. 카탈로그 플랫폼으로 진화시킬 때의 권장 아키텍처 (수정판)
ADB 안에 이미 카탈로그 도구들이 들어 있다는 점을 반영하면 그림이 한층 단순해집니다. 별도 제품을 사 붙일 필요 없이 ADB 한 통 안에서 끝납니다.
┌──────────────────────────────────────────────────────────────────┐
│ ADB (단일 박스) │
│ │
│ ① 탐색 / 메타데이터 / UI (전부 ADB 내장) │
│ ├─ Database Actions (웹 콘솔) │
│ ├─ Data Studio → Data Catalog (Entity, Glossary, Lineage) │
│ ├─ Data Studio → Sensitive Data Discovery (PII 자동 태깅) │
│ ├─ Data Studio → Data Insights (프로파일링) │
│ ├─ APEX (권한 관리 UI 노코드 구축) │
│ └─ ORDS (모든 객체 REST 자동 노출) │
│ │
│ ② 정책 평면 (오늘의 POC 가 추가한 부분) │
│ ├─ permission / group / user / source 테이블 │
│ ├─ Secure Application Context │
│ └─ Unified Audit (집행 결과·실패 시도 기록) │
│ │
│ ③ 집행 (ADB 엔진 안에서 자동 적용) │
│ ├─ VPD 정책 (행 단위) │
│ └─ Data Redaction (컬럼 단위, PII 마스킹) — 구현됨 │
│ │
│ ④ 데이터 소스 │
│ ├─ ADB 로컬 테이블 / 외부 테이블 (DBMS_CLOUD) │
│ └─ DB Link 로 묶인 RDS MySQL / Postgres / 다른 Oracle 등 │
└──────────────────────────────────────────────────────────────────┘
▲
│ 모든 SQL 클라이언트 (BI, JDBC, ORDS, sqlplus...)
이 그림이 보여 주는 핵심은: 카탈로그 + 집행 + 감사 + UI 가 한 박스 안에 있다. Unity Catalog 처럼 별도 컨트롤 플레인을 구입·설치할 필요가 없습니다.
11-7. POC 를 “진짜 카탈로그 시나리오” 로 확장하기
지금 POC 에 ADB 내장 카탈로그 기능을 결합하면 다음과 같은 엔드-투-엔드 시나리오 가 자연스럽게 완성됩니다. 이게 “카탈로그 + 거버넌스” 플랫폼이 실제 운영에서 어떻게 돌아가는지를 보여 주는 모습입니다.
시나리오: PII 자동 인식 → 자동 마스킹 + 행 권한
-
데이터 등록 (Data Studio Catalog) 데이터 엔지니어가
v_customers_pg,v_customers_my를 Data Catalog 에 “Customer” 라는 비즈니스 Entity 로 등록합니다. 용어집에 “Customer”, “Region” 같은 비즈니스 용어를 정의하고 매핑합니다. -
민감정보 자동 탐지 (Sensitive Data Discovery) ADB 가 두 뷰를 스캔해서
email,full_name컬럼을 PII 로 자동 태깅합니다. 사람이 컬럼 하나하나 확인할 필요가 없습니다. -
정책 정의 (우리의 권한 모델 + Oracle Data Redaction) 관리자가 카탈로그 UI 에서 결정합니다:
- 행 단위 (VPD): KR_ANALYSTS → APAC 만 — 구현됨
- 컬럼 단위 (Data Redaction): PII 로 태깅된 컬럼은 KR_ANALYSTS 에게는 마스킹(
a****@example.com,A****), GLOBAL_ADMINS 에게는 원본 그대로 — 구현됨 (sql/05a_redaction.sql)
-
사용자 질의 — 모든 통제가 자동 적용 VPDUSER_A 가
SELECT * FROM v_customers_pg한 줄을 던지면:- VPD 가
region IN ('APAC')을 자동으로 결합 → 행 필터링 - Data Redaction 이
email,full_name을 마스킹 → 컬럼 보호 - 두 정책 모두 사용자가 알지 못한 채 적용됨
- VPD 가
-
감사 (Unified Audit) 누가 언제 어떤 뷰를 조회했고, 어떤 정책이 적용됐고, 거부된 시도는 무엇이었는지 모두 기록됩니다.
-
거버넌스 운영 (APEX UI) 비-DBA 인 데이터 거버넌스 담당자가 APEX 로 만들어진 화면에서 권한을 부여/회수합니다. SQL 을 한 줄도 안 씁니다.
이 흐름이 완성되면 “우리 회사 데이터 카탈로그 플랫폼” 이라는 표현이 정당화 됩니다.
카탈로그 시나리오 확장 시 작업 우선순위
Data Redaction 정책 추가— 완료.sql/05a_redaction.sql에서email,full_name을 정규식 기반으로 마스킹. 정책 식은SYS_CONTEXT('VPD_CTX','<뷰명>') != '*'일 때만 마스킹 적용.- Data Studio Catalog 에 두 뷰 등록 + Term Glossary 정의 — UI 작업, 코딩 거의 없음.
- Sensitive Data Discovery 실행 — 한 번의 클릭으로 PII 자동 태깅.
- APEX 권한 관리 앱 —
permission테이블 위에 노코드 양식 폼 구축. (1~2일) - Unified Audit 정책 활성화 + 대시보드 — 감사 로그를 운영자가 볼 수 있게.
- OCI Data Catalog 와 연계 (선택) — 조직 전체의 다른 데이터 자산(데이터레이크, S3, OCI Object Storage 등)까지 시야를 넓힐 때.
11-8. 결론
| 질문 | 답 |
|---|---|
| ADB + VPD 를 “데이터 카탈로그 플랫폼” 이라고 말할 수 있는가? | 네, 정당화 가능합니다. Database Actions / Data Studio 안에 이미 카탈로그 도구(Catalog, SDD, Insights, Glossary) 가 내장되어 있고, 우리 POC 가 추가한 권한 집행 계층이 거버넌스의 마지막 퍼즐을 채웁니다. |
| Unity Catalog 와 동등한 제품이 되는가? | 포지셔닝이 다릅니다. Unity 는 “레이크하우스 + Spark 컴퓨트” 중심, 우리 구조는 “RDBMS·SQL 중심 거버넌스 + 집행 플랫폼”. 각각 잘 맞는 영역이 다릅니다. |
| 가장 큰 약점은? | ADB 를 거치지 않는 경로(예: Spark → RDS 직결, 객체 스토리지 직접 분석) 에 무력. 네트워크·아키텍처 정책으로 그 빈틈을 메워야 합니다. 계보(lineage) 도 ADB 가 보는 범위로 제한됩니다. |
| 가장 큰 강점은? | 집행이 SQL 파서 안에서 일어나서 앱 코드가 절대 우회할 수 없습니다. 그리고 모든 카탈로그 기능이 한 박스(ADB) 안에 있어 별도 제품 도입·통합 비용이 없습니다. |
| 다음 단계로 추천하는 것? | 11-7 의 6단계 — Data Redaction 은 이미 구현됨. 남은 항목 중 (1) Data Catalog 등록 + SDD (2) APEX 권한 관리 UI 까지만 추가해도 “카탈로그 플랫폼” 이라고 부르기 충분한 형태가 됩니다. |
마무리
이 POC가 보여주는 핵심은 단 하나입니다.
권한 정책을 데이터베이스 한 곳에 모아두고, 사용자의 SQL은 그대로 두면서 Oracle이 자동으로 알맞게 필터링한다.
사용자는 WHERE 절을 매번 쓸 필요가 없고, 개발자는 권한 로직을 앱 코드에 흩뿌릴 필요가 없고, 보안 담당자는 한 곳만 보면 누가 어디까지 보는지 알 수 있습니다. 그리고 위 6번에서 본 7 가지 계층이 함께 있어, “VPD 만 믿었는데 우회됐다” 같은 시나리오는 발생하기 어렵습니다.
운영 단계로 넘어갈 때는 7번 절의 강화 옵션과 8번 절의 한계 표를 점검 리스트로 활용하시면 됩니다.