Files
vpd-permission-poc/docs/03-detailed-guide.md
devmrko ed91306ee3 Pivot scenario from region-based 2 users to source-based 4 users
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>
2026-05-26 14:42:11 +09:00

48 KiB
Raw Permalink Blame History

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 하단의 주석을 해제하세요.


목차

  1. 무엇을 만들었고, 왜 만들었나
  2. 핵심 개념 풀어보기
  3. 전체 구조도
  4. 구성 요소별 설명
  5. 실제 동작 결과 (테스트 출력)
  6. “정말로 못 우회하나?” — 보안 보장 7 계층
  7. 추가 강화 옵션 (운영 단계용)
  8. 현재 POC의 한계와 운영 시 고려사항
  9. 디렉터리·파일 구조
  10. 실행 방법
  11. 데이터 카탈로그 인프라로서의 가능성 — 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 — 보안 컨텍스트와 패키지

세 가지가 한 번에 만들어집니다.

  1. ctx_pkg 패키지의 명세: init 이라는 프로시저가 있다고 선언.
  2. ctx_pkg 패키지의 본문: 실제 로직.
    • 지금 로그인한 Oracle 사용자 이름을 알아낸다 (SYS_CONTEXT('USERENV','SESSION_USER')).
    • 그 사람을 app_user 테이블에서 찾는다.
    • user_grouppermission 을 따라가서 “이 사람이 어느 객체에 어떤 region 까지 허용됐는지” 조회.
    • 결과를 VPD_CTX 컨텍스트 안에 객체별 속성으로 저장. (예: VPD_CTX.V_CUSTOMERS_PG = 'APAC')
  3. vpd_ctx 보안 컨텍스트 자체: USING ctx_pkg 라고 못 박아서, 다른 곳에서는 절대 이 값을 쓸 수 없게 막는다.

4-5. sql/04_views.sql — 외부 테이블에 대한 로컬 뷰

ADB 안에서 사용자가 보게 될 “테이블처럼 생긴 것”을 두 개 만듭니다. 둘 다 그저 외부 DB의 customers 테이블을 들여다보는 창문(view)일 뿐입니다.

  • v_customers_pg → PostgreSQL의 public.customers
  • v_customers_my → MySQL의 ecommerce_poc.customers

⚠️ 중요: 사용자에게는 이 뷰만 보여줍니다. 뒤에 있는 customers@RDS_POSTGRES_LINK 같은 원본 참조는 절대 직접 접근하게 두면 안 됩니다. 그래야 정책을 우회할 수 없습니다.

4-6. sql/05_policy.sql — VPD 정책 함수와 부착

이게 마법의 핵심입니다.

  1. 정책 함수 vpd_region_filter: 사용자의 컨텍스트에서 허용 region 을 읽고, region IN ('APAC') 같은 문자열을 돌려준다.
    • 만약 컨텍스트에 아무것도 없으면 → 1=0 을 돌려준다. (= “행 0개” = fail-closed 안전장치)
    • 만약 '*' 가 있으면 → NULL 을 돌려준다. (= 필터 없음 = 전부 보임)
  2. 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) 입장에서 “지금 어떤 정책들이 어느 뷰에 부착돼 있나?”, “누구한테 어떤 권한이 주어졌나?” 를 한눈에 보기 위한 감사용 스크립트. 다음 네 가지를 출력합니다.

  1. Attached VPD policiesDBA_POLICIES 에서 V_CUSTOMERS_PG, V_CUSTOMERS_MY 에 붙은 정책(CUSTOMERS_PG_POLICY, CUSTOMERS_MY_POLICY) 과 활성화 상태.
  2. Attached Data Redaction policiesREDACTION_POLICIES 에서 PII_REDACT_PG, PII_REDACT_MY 의 게이팅 식(SYS_CONTEXT('VPD_CTX','<뷰명>') IS NULL OR != '*') 을 그대로 확인.
  3. Redacted columnsREDACTION_COLUMNS 에서 어느 컬럼(EMAIL, FULL_NAME)이 어떤 정규식(^(.)(.*)(@.*)$\1****\3 등)으로 마스킹되는지 표시.
  4. Permission summaryapp_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

이 한 결과에 두 가지 보호 계층 이 동시에 보입니다.

  1. 행 단위 (VPD) — 원본은 5건이지만 2건만 보입니다. region IN ('NA','EMEA') 행은 VPDUSER_A 의 세션에서는 “존재 자체를 모릅니다.” WHERE region='NA' 같은 조건을 걸어도 0건이 나옵니다.
  2. 컬럼 단위 (Data Redaction) — 보이는 2건의 full_nameemail 도 원본이 아닙니다. Alice KimA****, alice.kim@example.coma****@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 테이블을 바꾸면, 이미 로그인되어 있는 사용자의 세션 컨텍스트는 그대로 입니다. 다음 로그인부터 적용됩니다. 즉시 반영이 필요하면 두 가지 방법이 있습니다.

  1. 세션 강제 종료: ADMIN 이 변경 후 그 사용자 세션을 ALTER SYSTEM KILL SESSION 으로 끊는다. 다음 로그인 때 새 권한 적용.
  2. 정책 함수를 컨텍스트가 아닌 테이블 직접 조회로 작성: 매 쿼리마다 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 에는 아무 흔적이 안 남습니다. 이 빈틈은 두 가지로 메웁니다.

  1. 아키텍처 규칙: “이 데이터 소스들에 대한 모든 접근은 ADB 를 통해야 한다” 를 조직 규칙으로 못 박는다.
  2. 네트워크 통제: RDS 인스턴스를 외부에서 직접 못 부르도록 보안그룹/VPC 단에서 잠근다. 오직 ADB 만이 RDS 까지 도달 가능하게.

기술적 보장이 아니라 설계 결정입니다. 충분히 달성 가능하지만 기술이 자동으로 막아주는 게 아니라는 점을 인지하셔야 합니다.

11-4. ADB+VPD 를 카탈로그 인프라로 쓸 때의 진짜 강점

  1. 집행이 수학적으로 보장된다. VPD 는 SQL 파서 단계에서 적용됩니다. 어떤 클라이언트도, 어떤 BI 도구도, 어떤 드라이버도 이 정책을 건너뛸 수 없습니다. 앱 코드가 “정책 함수 호출하는 걸 깜빡했어요” 같은 사고가 원천 차단됩니다.
  2. 데이터 이동이 필요 없다. Unity Catalog 의 진가는 데이터가 Databricks 의 Delta Lake 안에 있을 때 발휘됩니다. ADB+VPD 는 MySQL 은 MySQL 인 채로, Postgres 는 Postgres 인 채로 통합 거버넌스를 줍니다.
  3. 다종(polyglot) RDBMS 환경에 강하다. 이종 DB Link 와 Database Gateway 로 Oracle + MySQL + Postgres + SQL Server + Db2 등을 한 시야에서 묶을 수 있습니다.
  4. SQL 네이티브. Oracle Net 이나 JDBC 만 말할 줄 알면 어떤 BI 도구든 그대로 붙습니다. 별도 커넥터를 만들 필요가 없습니다.
  5. 규제 산업 (금융·보험·의료) 에 매우 적합합니다. “페타바이트 스케일” 보다 “감사 추적과 증명 가능한 집행” 이 더 중요한 영역에서 이 구조는 자연스럽습니다.

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 자동 인식 → 자동 마스킹 + 행 권한

  1. 데이터 등록 (Data Studio Catalog) 데이터 엔지니어가 v_customers_pg, v_customers_my 를 Data Catalog 에 “Customer” 라는 비즈니스 Entity 로 등록합니다. 용어집에 “Customer”, “Region” 같은 비즈니스 용어를 정의하고 매핑합니다.

  2. 민감정보 자동 탐지 (Sensitive Data Discovery) ADB 가 두 뷰를 스캔해서 email, full_name 컬럼을 PII 로 자동 태깅합니다. 사람이 컬럼 하나하나 확인할 필요가 없습니다.

  3. 정책 정의 (우리의 권한 모델 + Oracle Data Redaction) 관리자가 카탈로그 UI 에서 결정합니다:

    • 행 단위 (VPD): KR_ANALYSTS → APAC 만 — 구현됨
    • 컬럼 단위 (Data Redaction): PII 로 태깅된 컬럼은 KR_ANALYSTS 에게는 마스킹(a****@example.com, A****), GLOBAL_ADMINS 에게는 원본 그대로 — 구현됨 (sql/05a_redaction.sql)
  4. 사용자 질의 — 모든 통제가 자동 적용 VPDUSER_A 가 SELECT * FROM v_customers_pg 한 줄을 던지면:

    • VPD 가 region IN ('APAC') 을 자동으로 결합 → 행 필터링
    • Data Redaction 이 email, full_name 을 마스킹 → 컬럼 보호
    • 두 정책 모두 사용자가 알지 못한 채 적용됨
  5. 감사 (Unified Audit) 누가 언제 어떤 뷰를 조회했고, 어떤 정책이 적용됐고, 거부된 시도는 무엇이었는지 모두 기록됩니다.

  6. 거버넌스 운영 (APEX UI) 비-DBA 인 데이터 거버넌스 담당자가 APEX 로 만들어진 화면에서 권한을 부여/회수합니다. SQL 을 한 줄도 안 씁니다.

이 흐름이 완성되면 “우리 회사 데이터 카탈로그 플랫폼” 이라는 표현이 정당화 됩니다.

카탈로그 시나리오 확장 시 작업 우선순위

  1. Data Redaction 정책 추가완료. sql/05a_redaction.sql 에서 email, full_name 을 정규식 기반으로 마스킹. 정책 식은 SYS_CONTEXT('VPD_CTX','<뷰명>') != '*' 일 때만 마스킹 적용.
  2. Data Studio Catalog 에 두 뷰 등록 + Term Glossary 정의 — UI 작업, 코딩 거의 없음.
  3. Sensitive Data Discovery 실행 — 한 번의 클릭으로 PII 자동 태깅.
  4. APEX 권한 관리 앱permission 테이블 위에 노코드 양식 폼 구축. (1~2일)
  5. Unified Audit 정책 활성화 + 대시보드 — 감사 로그를 운영자가 볼 수 있게.
  6. 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번 절의 한계 표를 점검 리스트로 활용하시면 됩니다.