내용 목차
본 장에서는 Tibero의 보안상 혹은 특정 서비스를 목적으로 필요한 유저에 해당하는 데이터만 다룰 수 있게 하는 기술인 Virtual Private Database에 대해서 설명한다.
Virtual Private Database는 행과 열 수준에서 데이터베이스 접근을 제어하는 보안 정책을 생성한다.
기본적으로 Virtual Private Database는 보안 정책이 적용된 테이블, 뷰, 또는 동의어에 대해 실행되는 SQL 문에 동적 WHERE 절을 추가한다. Virtual Private Database는 테이블, 뷰, 동의어에 대해 직접 세부 수준까지 보안을 강화한다. 보안 정책을 이러한 데이터베이스 객체에 직접 연결하고 사용자가 데이터에 접근할 때 마다 정책이 자동으로 적용되기 때문에 보안을 우회할 방법이 없다.
사용자가 Virtual Private Database 정책을 보호되는 테이블, 뷰, 동의어를 직접 또는 간접적으로 접근할 때 데이터베이스는 사용자의 SQL 문을 동적으로 수정한다. 이 수정은 보안 정책을 구현하는 함수가 반환하는 WHERE 문을 생성한다. 이러한 Virtual Private Database 정책은 SELECT, INSERT, UPDATE, DELETE 문에 적용할 수 있다.
Virtual Private Database는 보안성, 단순성 및 유연성 면에서 이점을 제공한다.
모든 애플리케이션에서 접근 제어를 구현하는 대신 Virtual Private Database 보안 정책을 활용하면 다음과 같은 이점이 있다.
보안성
Virtual Private Database는 심각한 애플리케이션 보안 문제를 해결할 수 있다. 세분화된 접근 제어는 어떻게 사용자가 데이터에 접근하든 똑같은 보안 정책을 적용시킬 수 있다. 데이터에 대한 원천적인 보안 설정이 가능하다.
단순성
특정 테이블, 뷰, 동의어를 위한 보안 설정을 할 필요 없이 보안 정책을 한번에 설정할 수 있다.
유연성
SELECT, INSERT, UPDATE, DELETE에 따라 다른 보안 정책을 만들 수 있다. 또한 테이블, 뷰, 동의어에 여러 정책을 생성할 수 있다.
Virtual Private Database를 만들려면 동적 WHERE 문을 생성하는 정책 함수와 함수를 객체로 연결하는 보안 정책이 필요하다.
정책 함수
정책 함수는 다음과 같은 조건을 만족해야 한다.
스키마 이름과 객체 이름을 인수로 입력해야 한다.
WHERE 문의 조건문을 항상 반환해야 한다. 항상 반환 값은 VARCHAR 타입이여야 한다.
유효한 WHERE 문을 생성해야 한다.
연관된 정책 함수 내의 테이블을 선택하면 안된다.
연관 되지 않는 함수이여야 한다. 함수는 패키지 변수에 종속되지 않아야 한다.
예제
CREATE OR REPLACE FUNCTION F_vpd_select(schema varchar2, object varchar2) RETURN varchar2 IS BEGIN RETURN 'mod( c1, 2) != 0'; END; /
보안 정책
DBMS_RLS 패키지를 통해서 보안 정책을 만들 수 있다. 사용자는 SYS 계정이거나 DBMS_RLS 패키지를 EXECUTE 할 수 있는 권한이 있어야 한다. DBMS_RLS.ADD_POLICY로 정책을 만들 수 있다.
예제
BEGIN DBMS_RLS.ADD_POLICY( object_name => 'T_vpd_select', policy_name => 'policy_select', policy_function => 'F_vpd_select', statement_types => 'select' ); END; /
Virtual Private Database를 제대로 구성하기 위해서는 Application Context와 Policy를 알아야 한다.
Application Context는 사용자 식별 정보를 저장해준다. 이 사용자 식별 정보를 이용해서 데이터 접근 권한을 관리할 수 있다. Virtual Private Database는 정책 함수에 Application Context를 적용하여 사용자에 따라서 다른 결과를 보여줄 수 있다.
namespace : Application Context의 이름 (예: orders_ctx)
name : 속성의 이름 (예: cust_no)
value : 속성의 값 (예: 1)
문맥 네임스페이스를 생성(혹은 수정)하고 해당 네임스페이스의 문맥을 조작할 수 있는 패키지를 지정한다. 지정된 패키지에서 DBMS_SESSION.SET_CONTEXT 프러시저를 사용해 해당 네임스페이스의 속성 값들을 설정할 수 있다.
예제
orders_ctx란 Application Context를 생성한다. orders_ctx_proc란 프러시저로 orders_ctx를 보안 정책을 구현할 수 있다.
CREATE CONTEXT orders_ctx USING orders_ctx_proc;
CREATE CONTEXT에 대한 자세한 내용은 "Tibero SQL 참조 안내서"의 "7.26 CREATE CONTEXT"를 참고한다.
이 프러시저는 CREATE CONTEXT DDL을 통해 지정된 패키지 내에서만 호출할 수 있다. 설정된 속성 값은 세션이 유지되는 동안 보존된다. 만일 해당 문맥의 속성이 이미 설정되어 있을 경우 SET_CONTEXT 호출은 해당 속성값을 덮어쓴다.
예제
orders_ctx_proc 프러시저를 정의한다. 패키지 프러시저 안에 DBMS_SESSION.SET_CONTEXT를 활용해서 orders_ctx의 Application Context를 설정한다.
create or replace procedure orders_ctx_proc( val in VARCHAR2) as begin dbms_session.set_context ('orders_ctx', 'cust_no', val); end; / EXEC orders_ctx_proc('1');
DBMS_SESSION.SET_CONTEXT에 대한 자세한 내용은 "Tibero tbPSM 참조 안내서"의 "36.2.5 SET_CONTEXT"를 참고한다.
SYS_CONTEXT는 문맥 네임스페이스(CONTEXT NAMESPACE)와 관련된 파라미터의 값을 반환하는 함수이다. 문맥 네임스페이스와 파라미터는 문자열이나 표현식으로 정의할 수 있으며, 함수의 반환값은 VARCHAR 타입이다. Tibero에서 디폴트로 제공하고 있는 문맥 네임스페이스는 USERENV이다.
예제
orders_ctx의 cust_no 속성의 값을 반환한다.
SQL> SELECT SYS_CONTEXT('orders_ctx', 'cust_no') cust_no FROM DUAL; CUST_NO ------- 1 1 row selected.
SYS_CONTEXT에 대한 자세한 내용은 "Tibero SQL 참조 안내서"의 "4.2.159 SYS_CONTEXT"를 참고한다.
Virtual Private Database의 정책은 정책 함수에 따라 WHERE 문을 바꿔주며 이 정책을 구성하기 위해선 다음 DBMS_RLS 패키지를 활용한다.
Procedure | 설명 |
---|---|
DBMS_RLS.ADD_POLICY | 테이블, 뷰, 동의어에 정책을 추가한다. |
DBMS_RLS.DROP_POLICY | 테이블, 뷰, 동의어에 걸려있는 보안 정책을 제거한다. |
DBMS_RLS.ENABLE_POLICY | 테이블, 뷰, 동의어에 걸려있는 보안 정책을 활성화 또는 비활성화한다. |
DBMS_RLS.REFRESH_POLICY | 해당 보안 정책으로 인해 영향을 받았던 실행 계획들과 메모리에 저장되어 있던 정책 함수의 결과들을 모두 무효화 시킨다. |
DBMS_RLS에 대한 자세한 내용은 "Tibero tbPSM 참조 안내서"의 "제33장 DBMS_RLS"를 참고한다.
본 절에서는 Application Context 없는 간단한 VPD와 Application Context 있는 VPD를 생성하는 간단한 예제를 설명한다.
다음은 Application Context 없는 VPD를 생성하는 과정에 대한 설명이다.
User 생성 및 권한 부여
CONN sys/tibero; CREATE USER user1 IDENTIFIED BY tibero; GRANT CREATE SESSION TO user1;
Table 생성
CREATE TABLE orders ( order_no number(4), item varchar(256) ); INSERT INTO orders VALUES(1, 'apple'); INSERT INTO orders VALUES(1, 'banana'); INSERT INTO orders VALUES(2, 'candy'); INSERT INTO orders VALUES(2, 'dog'); INSERT INTO orders VALUES(3, 'elephant'); INSERT INTO orders VALUES(3, 'frog'); INSERT INTO orders VALUES(4, 'giraffe'); INSERT INTO orders VALUES(4, 'hawk'); GRANT SELECT on orders TO user1;
정책 함수 생성
CREATE OR REPLACE FUNCTION policy_func1( schema_var IN VARCHAR2, table_var IN VARCHAR2 ) RETURN VARCHAR2 IS return_val VARCHAR2 (400); BEGIN return_val := 'order_no = 1'; RETURN return_val; END policy_func1; /
VPD 정책 생성
BEGIN DBMS_RLS.ADD_POLICY ( object_schema => 'sys', object_name => 'orders', policy_name => 'cust_policy', function_schema => 'sys', policy_function => 'policy_func1', statement_types => 'select' ); END; /
정책 확인
CONN user1/tibero; SELECT * FROM sys.orders; ORDER_NO ITEM ---------- -------------------- 1 apple 1 banana 2 rows selected.
예제 객체 제거
CONN sys/tibero; DROP FUNCTION policy_func1; EXEC DBMS_RLS.DROP_POLICY('SYS','ORDERS','CUST_POLICY'); DROP TABLE orders; DROP USER user1;
다음은 Application Context 있는 VPD를 생성하는 과정에 대한 설명이다.
User 생성 및 권한 부여
CONN SYS/TIBERO; CREATE USER sys_vpd IDENTIFIED BY tibero; GRANT CREATE SESSION, CREATE ANY CONTEXT, CREATE PROCEDURE, CREATE TRIGGER, ADMINISTER DATABASE TRIGGER TO sys_vpd; GRANT EXECUTE ON DBMS_SESSION TO sys_vpd; GRANT EXECUTE ON DBMS_RLS TO sys_vpd; CREATE USER user1 IDENTIFIED BY tibero; CREATE USER user2 IDENTIFIED BY tibero; CREATE USER user3 IDENTIFIED BY tibero; CREATE USER manager IDENTIFIED BY tibero; GRANT CREATE SESSION TO user1; GRANT CREATE SESSION TO user2; GRANT CREATE SESSION TO user3; GRANT CREATE SESSION TO manager; GRANT CONNECT, RESOURCE TO manager;
Table 생성
CONN manager/tibero; CREATE TABLE customers ( cust_no number(4), cust_id varchar(20), cust_name varchar(20) ); INSERT INTO customers VALUES (1, 'USER1', 'Park'); INSERT INTO customers VALUES (2, 'USER2', 'Kim'); INSERT INTO customers VALUES (3, 'USER3', 'Lee'); GRANT SELECT ON customers TO sys_vpd; CREATE TABLE orders ( order_no number(4), user_id varchar(256), item varchar(256) ); INSERT INTO orders VALUES(1, 'USER1', 'apple'); INSERT INTO orders VALUES(1, 'USER1', 'banana'); INSERT INTO orders VALUES(2, 'USER2', 'candy'); INSERT INTO orders VALUES(2, 'USER2', 'dog'); INSERT INTO orders VALUES(3, 'USER3', 'elephant'); INSERT INTO orders VALUES(3, 'USER3', 'frog'); INSERT INTO orders VALUES(4, 'USER4', 'giraffe'); INSERT INTO orders VALUES(4, 'USER4', 'hawk'); GRANT SELECT ON orders TO user1; GRANT SELECT ON orders TO user2; GRANT SELECT ON orders TO user3;
Application Context 생성
CONN sys_vpd/tibero; CREATE OR REPLACE CONTEXT orders_ctx USING orders_ctx_pkg;
Application Context 생성을 위한 PL/SQL Package 생성
CREATE OR REPLACE PACKAGE orders_ctx_pkg IS PROCEDURE set_custnum; END; / CREATE OR REPLACE PACKAGE BODY orders_ctx_pkg IS PROCEDURE set_custnum AS custnum NUMBER; BEGIN SELECT cust_no INTO custnum FROM MANAGER.CUSTOMERS WHERE cust_id = SYS_CONTEXT('USERENV', 'SESSION_USER'); DBMS_SESSION.SET_CONTEXT('orders_ctx', 'cust_no', custnum); EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END set_custnum; END; /
Logon Trigger 생성
CREATE TRIGGER set_custno_ctx_trig AFTER LOGON ON DATABASE BEGIN sys_vpd.orders_ctx_pkg.set_custnum; END; /
Logon Trigger 테스트
CONN user1/tibero; SELECT SYS_CONTEXT('orders_ctx', 'cust_no') custnum FROM DUAL; CUSTNUM ---------------------------------------------------------------- 1 1 row selected. CONN user2/tibero; SELECT SYS_CONTEXT('orders_ctx', 'cust_no') custnum FROM DUAL; CUSTNUM ---------------------------------------------------------------- 2 1 row selected. CONN user3/tibero; SELECT SYS_CONTEXT('orders_ctx', 'cust_no') custnum FROM DUAL; CUSTNUM ---------------------------------------------------------------- 3 1 row selected.
정책 함수 생성
CONN sys_vpd/tibero; CREATE OR REPLACE FUNCTION get_user_orders( schema_p IN VARCHAR2, table_p IN VARCHAR2) RETURN VARCHAR2 AS orders_pred VARCHAR2 (400); BEGIN orders_pred := 'order_no = SYS_CONTEXT(''orders_ctx'', ''cust_no'')'; RETURN orders_pred; END; /
VPD 정책 생성
BEGIN DBMS_RLS.ADD_POLICY ( object_schema => 'manager', object_name => 'orders', policy_name => 'cust_policy', function_schema => 'sys_vpd', policy_function => 'get_user_orders', statement_types => 'select'); END; /
정책 확인
CONN user1/tibero; SELECT * FROM manager.orders; ORDER_NO USER_ID ITEM ---------- -------------------- -------------------- 1 USER1 apple 1 USER1 banana 2 rows selected. CONN user2/tibero; SELECT * FROM manager.orders; ORDER_NO USER_ID ITEM ---------- -------------------- -------------------- 2 USER2 candy 2 USER2 dog 2 rows selected. CONN user3/tibero; SELECT * FROM manager.orders; ORDER_NO USER_ID ITEM ---------- -------------------- -------------------- 3 USER3 elephant 3 USER3 frog 2 rows selected.
예제 객체 제거
CONN sys_vpd/tibero; DROP FUNCTION get_user_orders; EXEC DBMS_RLS.DROP_POLICY('MANAGER','ORDERS','CUST_POLICY'); DROP CONTEXT orders_ctx; CONN manager/tibero; DROP TABLE orders; DROP TABLE customers; CONN sys/tibero; DROP USER user1; DROP USER user2; DROP USER user3; DROP USER sys_vpd; DROP USER manager;