IT/ORACLE

데이터베이스 인프라스트럭처의 보안을 위한 단계별 접근법

Qhtlr 2007. 4. 17. 14:02

1 단계

기간: 1 일

보안/컴플라이언스 프로젝트의 1 단계에서는, 24시간 이내에 인프라스트럭처 보안 체계를 구현하기 위한 방법을 알아 봅니다.

목차:
· 1.1 디폴트 패스워드의 제거
· 1.2 오라클 바이너리 권한 설정
· 1.3 다른 실행 파일의 보안
· 1.4 umask의 사용
· 1.5 SYSDBA 로그인의 제한
· 1.6 Listener 패스워드의 생성
· 1.7 Listener의 보호
· 1.8 과도한 권한의 제한
· 1.9 DBSNMP 패스워드의 변경

1.1 디폴트 패스워드의 제거

배경

오라클 소프트웨어를 설치하고 데이터베이스를 생성하는 과정에서 생성된 계정을 잊고 방치하는 경우가 많습니다. 이러한 계정에는 디폴트 패스워드(예: SCOTT/tiger)가 그대로 사용되므로 침입자들의 진입 경로로 즐겨 활용됩니다. 필자가 진단한 운영 데이터베이스 중에서 SYS 계정의 암호로 “change_on_install” 또는 “oracle”을 그대로 사용하는 경우가 얼마나 많았는지 안다면 놀라실 수 밖에 없을 것입니다. 그러므로 이러한 디폴트 패스워드를 가장 먼저 확인하고 제거해야 합니다.

전략

디폴트 패스워드가 설정된 계정을 어떻게 확인할까요? 디폴트 패스워드를 사용해서 각 계정에 로그인을 시도하는 것이 한 가지 방법입니다. 하지만 이것은 매우 번거로운 방법일 뿐 아니라 시간적으로도 낭비입니다.

좀 더 간편한 대안이 있습니다. DBA_USERS 뷰의 패스워드 컬럼을 조회해 봅시다:

SQL> select username, password
  2  from dba_users
  3  where username = 'SCOTT';
USERNAME                       PASSWORD
------------------------------ ------------------
SCOTT                          F894844C34402B67

패스워드가 해쉬 처리 되어 있기 때문에 해독이 불가능하지만, SCOTT의 암호가 “tiger”라는 사실은 알고 있습니다. 따라서 사용자 아이디가 “scott”인 경우 “tiger”의 해쉬값은 F894844C34402B67입니다. SCOTT의 패스워드가 변경되면 해쉬값도 역시 변경됩니다. DBA_USERS 뷰를 통해 SCOTT의 암호가 이 해쉬값과 일치하는지 확인하면 암호가 “tiger”인지 알 수 있습니다. 하지만 패스워드의 문자열 만으로 해쉬값이 결정되는 것은 아니라는 사실을 참고하실 필요가 있습니다. 다른 사용자가 “tiger”라는 암호를 갖는 경우, 그 해쉬값은 달라지게 됩니다..

SQL> create user scott2 identified by tiger;

User created.

SQL> select username, password
  2  from dba_users
  3  where username = 'SCOTT2';

USERNAME                       PASSWORD
------------------------------ --------------------
SCOTT2                         C44C11D4C34DB67D

위의 예와 같이, 패스워드가 동일함에도 다른 해쉬값( C44C11D4C34DB67D)이 표시되고 있습니다.

그렇다면 이 정보를 어떻게 활용할 수 있을까요? 간단합니다. 디폴트 사용자를 디폴트 패스워드로 설정한 경우, 패스워드의 해쉬 값을 미리 알 수 있습니다. 미리 알고 있는 계정과 디폴트 패스워드의 해쉬값 목록을 테이블 형태로 작성하고 데이터 딕셔너리에 저장된 패스워드 해쉬와 비교하면 됩니다.

2006년 1월, 오라클은 디폴트 패스워드와 사용자를 확인하기 위한 유틸리티를 작성하여 다운로드 형식으로 배포하였습니다. 이 유틸리티는 MetaLink 문서ID 340009에서 확인할 수 있습니다. 유틸리티는 위에서 설명한 것과 유사한 방법으로 몇 가지 디폴트 계정에 대한 점검 작업을 수행합니다. 하지만 여러분이 이 문서를 읽고 있을 즈음에는 더욱 개선된 기능이 제공되고 있을 가능성이 높습니다.

또 보안 전문가 피트 피니건(Pete Finnigan)이 오라클 및 써드 파티 프로그램의 설치 과정에서 생성되는 디폴트 계정의 정보를 수집하여 개인 웹 사이트에 게시하고 있습니다. (법적고지사항: 오라클은 써드 파티 웹 사이트의 컨텐트에 대한 보증을 제공하지 않습니다.) 다른 이들의 수고를 덜어준 피트에게 이 자리를 빌어 감사 드립니다. 하지만 본 문서에서는 피트와는 약간 다른 접근법을 사용하고 있습니다.

먼저, 디폴트 계정과 디폴트 패스워드를 저장할 테이블을 생성합시다.

CREATE TABLE osp_accounts
(
   product          VARCHAR2(30),
   security_level   NUMBER(1),
   username         VARCHAR2(30),
   password         VARCHAR2(30),
   hash_value       VARCHAR2(30),
   commentary       VARCHAR2(200)
)

다음으로 피트가 수집한 데이터를 테이블에 로드합니다. (스크립트는 이곳에서 다운로드할 수 있습니다.) 테이블이 로드되었다면, 디폴트 패스워드를 검색할 준비를 모두 마친 셈입니다. 간단한 SQL 구문을 사용하여 사용자를 검색해 봅시다.)

col password format a20
col account_status format a20
col username format a15
select o.username, o.password, d.account_status
from dba_users d, osp_accounts o
where o.hash_value = d.password
/
USERNAME PASSWORD ACCOUNT_STATUS --------------- -------------------- -------------------- CTXSYS CHANGE_ON_INSTALL OPEN OLAPSYS MANAGER OPEN DIP DIP EXPIRED & LOCKED DMSYS DMSYS OPEN EXFSYS EXFSYS EXPIRED & LOCKED SYSTEM ORACLE OPEN WMSYS WMSYS EXPIRED & LOCKED XDB CHANGE_ON_INSTALL EXPIRED & LOCKED OUTLN OUTLN OPEN SCOTT TIGER OPEN SYS ORACLE OPEN

위에서, 가장 우려했던 문제가 발견되었습니다. 마지막 라인에서 SYS의 패스워드가 “ORACLE”로 설정되어 있습니다! “change_on_install”보다는 낫겠지만, 그래도 쉽게 알아맞힐 수 있는 패스워드임에는 분명합니다.

패스워드와 관련된 보안 취약점은 버전에 따라 달라집니다. Oracle Database 10g와 이후 버전에서는 데이터베이스 설치 과정에서 패스워드를 “change_on_install” 대신 다른 것으로 설정할 것인지 묻는 프롬프트가 뜹니다. 이처럼 사용자에게 선택을 강요하는 환경에서는 패스워드가 디폴트로 설정되지 않았을 가능성이 높습니다. 하지만, 사용자가 “oracle”처럼 뻔한 암호를 선택한다면, 그 취지 또한 무색해집니다. (아마도 “oracle”이라는 암호는 운영 환경으로의 전환이 이루어지기 전에 DBA가 편의상 설정해 둔 것으로 짐작됩니다.)

Oracle Database 10g 이전 버전에서는 패스워드를 입력하는 프롬프트가 표시되지 않으며, 따라서 디폴트 패스워드(예: SYS/“change_on_install”, SYSTEM/”manager”)가 여전히 활성화되어 있을 확률이 큽니다. 이 툴을 이용하면 이러한 사례를 쉽게 확인할 수 있습니다.

또 SCOTT(SQL 학습을 위한 데모 계정)이라는 아이디는 개발용 데이터베이스에서는 문제가 없겠지만 운영 데이터베이스에는 적절하지 않은 계정입니다. SCOTT 계정은 침입을 위한 백도어로 사용될 수 있으므로 설치 후 바로 삭제하는 것이 좋습니다.

그 밖에 오라클 툴에 의해 사용되는 계정으로 CTXSYS, DMSYS, OLAPSYS 등이 있습니다. 관련 옵션을 사용하지 않는다면 이 계정들 역시 삭제하는 것이 권장됩니다. 이 계정의 사용 여부를 확신할 수 없거나 만약을 위해 남겨두고자 한다면, 계정을 잔류시키되 외부 연결로부터 잠금 설정을 하는 것이 가능합니다. 계정을 잠그고 암호를 만료 처리하기 위한 구문이 아래와 같습니다:

alter user dmsys account lock expire password;

위 구문을 실행하면 계정이 EXPIRED & LOCKED 상태로 설정되었습니다. 사용자가 로그인을 시도하면 아래와 같은 에러가 발생합니다.

ERROR:
ORA-28000: the account is locked
Warning: You are no longer connected to ORACLE.

잠금 설정을 할 수 없는 계정에 대해서는 패스워드를 변경해 줍니다. 그 한 가지 예로 DBNSMP를 들 수 있습니다 (여기에 대해서는 뒷부분에서 다시 설명합니다.)

주의 사항
사용되지 않는 계정을 잠그는 것으로 인해 문제가 발생할 이유는 없습니다.

실행 계획
사용되지 않는 계정의 검색.
계정 잠금 및 패스워드 만료 처리.


1.2 오라클 바이너리 권한의 설정

배경 정보
오라클 데이터베이스는 여러 가지 바이너리 파일을 사용합니다. 그 중 가장 중요한 파일로 UNIX/Linux의 “oracle”, Windows의 “oracle.exe”를 꼽을 수 있습니다.

이 파일들의 실행 권한을 확인해 봅시다. UNIX에서는 다음과 같은 결과를 얻을 수 있습니다.

# cd $ORACLE_HOME/bin
# ls -l oracle
-rwsr-s--x   1 oracle oinstall      69344968 Jun 10 14:05 oracle

실행 권한은 디폴트 값으로 설정되어 있습니다. (권한은 오라클 버전에 관계없이 동일하게 설정됩니다.) 각각의 설정이 의미하는 바를 확인해 보시다 (UNIX 실행 권한의 개념에 익숙하다면, 이 섹션을 생략하고 "2-태스크 아키텍처"로 바로 진행하셔도 됩니다.)

첫 번째 문자는 파일의 타입을 의미합니다. UNIX에서는 모든 것(정규 파일, 디렉토리, 디바이스 등)이 파일로 취급됩니다. 위의 경우 “-“으로 표시되고 있으므로 정규 파일임을 알 수 있습니다. 디렉토리의 경우는 “d”, 특수 문자 디바이스인 경우에는 “c” 등으로 표시됩니다.

두 번째 이후의 문자는 파일에 설정된 권한을 의미합니다. 권한은 3개 문자 단위로 표시되며 각각 읽기,쓰기,실행 권한의 설정 상태를 의미합니다. 앞의 세 문자는 소유자(owner)의 권한을, 그 다음 세 문자는 파일이 소속된 그룹(group)의 권한을, 그리고 마지막 세 문자는 다른 모든 사용자에 제공되는 권한을 표시합니다.

위치

1

2

3

4

5

6

7

8

9

10

-

r

w

s

r

-

s

-

-

x

   

Owner

Group

Other

각 권한은 특정 값 또는 “-“의 값을 갖습니다. “-“은 해당 권한이 부여되지 않았음을 의미합니다. 실행 예의 6 번째 위치에서 Group에 대한 쓰기 권한이 “-“로 표시되어 있습니다. 따라서 (파일이 소속된) “dba” 그룹은 이 파일에 쓰기 작업을 수행할 수 없습니다. 권한이 부여된 경우에는, 해당되는 값이 문자로 표시됩니다. 실행 예에서 그룹의 읽기 권한(5번째 문자)이 “r”로 표시되어 있으므로 “dba” 그룹은 이 파일을 읽을 수 있습니다.

마지막 3 개의 문자는 다른 사용자(oracle 사용자 또는 “dba” 그룹에 속하지 않은 사용자)들의 권한을 의미합니다. 실행 예에서 다른 사용자들은 이 파일을 실행할 수 있으나 읽거나 쓸 수 없습니다.

“r”, “w”, “x”는 각각 읽기(read), 쓰기(write), 실행(execute)를 의미합니다. 그렇다면 “x” 대신 표시된 “s”는 무엇을 의미할까요? 위에서 “s”는 프로그램이 setid에 대해 활성화되어 있음을 뜻합니다. setid가 활성화되어 있는 경우, 프로그램이 어떤 사용자에 의해 실행되든 실제로 프로그램을 소유한 사용자(“oracle”)의 이름으로 실행됩니다. 따라서 오라클 소프트웨어가 프로그램을 소유하고 있지만 오라클에 접속하는 어떤 사용자든 프로그램의 실행이 가능한 것입니다. 또 프로그램이 다른 사용자가 아닌 “oracle”의 권한으로 실행되므로, 데이터베이스 파일의 오픈과 같은 작업이 가능합니다.

“Two-Task” 아키텍처. 오라클 데이터베이스가 사용자 프로세스와 서버 프로세스를 구분하여 실행한다는 사실을 기억하고 계실 줄 압니다. 기억이 잘 나지 않는다면, Oracle Database 10g Concepts Manual 의 앞 부분 몇 챕터를 읽어 보시기 바랍니다. 이 자료는 오라클 데이터베이스의 실행 방식에 대해 매우 간략한 형태로 설명하고 있으며, 권한에 대한 기본적인 개념을 이해하는데 도움이 됩니다. (하지만 제품 매뉴얼의 내용만큼 자세하지는 않습니다.)

사용자가 SQL*Plus와 같은 프로그램을 사용하여 오라클 데이터베이스에 접속하는 경우, 오라클은 사용자 프로그램에 서비스를 제공하기 위해 새로운 프로세스를 생성합니다. 이 새로운 프로세스는 Oracle server process라 불리며 사용자 프로세스(sqlplus, splplus.exe, TOAD.exe 등)와 구분됩니다. 이 서버 프로세스는 SGA(System Global Area)와 같은 메모리 구조에 접근하며, SGA의 데이터 블록 버퍼에 데이터가 존재하지 않는 경우 데이터 파일로부터 직접 데이터를 읽어 들입니다. 어떠한 경우에도 사용자 프로세스(sqplus)가 오라클 데이터베이스에 직접 접근할 수는 없습니다. 작업을 완료하기 위해 두 가지 프로세스(사용자 프로세스와 서버 프로세스)가 병렬적으로 실행되므로, 이러한 구조를 “two-task” 아키텍처라 부르기도 합니다. 사용자 프로세스가 수행한 작업으로 인해 데이터베이스 손상의 위험이 있는 경우(예: 호스트 머신의 메모리 관리를 위반하는 경우)에도 오라클 데이터베이스는 영향을 받지 않으며, 피해 범위는 사용자 프로세스로만 한정됩니다.

(위의 설명은 “dedicated” 서버 환경의 오라클 연결에만 적용됨을 참고하시기 바랍니다. “multi-threaded” 서버 환경에서는 단일 서버 프로세스가 두 개 이상의 사용자 프로세스에 서비스를 제공할 수 있으므로 그 개념이 다소 달라집니다. “two-task” 아키텍처는 여전히 유효하지만, 서버와 사용자 프로세스의 관계는 1:1이 아닌 1:N으로 구성됩니다.)

서버 프로세스는 오라클 소프트웨어를 소유한 사용자의 이름으로 실행됩니다. 그 예가 아래와 같습니다. 사용자가 SQL*Plus를 이용하여 데이터베이스에 로그인했다고 가정해 봅시다.

$ sqlplus arup/arup

다음으로 프로세스를 조회해 봅시다:

$ ps -aef|grep sqlplus

그러면 아래와 같은 결과가 표시됩니다:

oracle 6339 6185 0 13:06 pts/0 00:00:00 sqlplus

여기서는 다른 SQL*Plus 세션이 서버에서 실행 중이지 않음을 가정하고 있습니다.

이번에는 프로세스 아이디 6339를 기준으로 다시 조회해 보겠습니다.

$ ps -aef|grep 6339

두 개의 프로세스가 결과로 출력됩니다:

oracle    6339  6185  0 13:06 pts/0    00:00:00 sqlplus
oracle    6340  6339  0 13:06 ?        00:00:00 oracleDBA102 (DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq))) 

첫 번째 프로세스(SQL*Plus 세션을 위한 프로세스)는 이미 우리가 잘 알고 있는 것입니다. 두 번째 프로세스(프로세스 ID 6340)은 오라클이 사용자를 위해 생성한 서버 프로세스입니다. 여기서 프로세스의 Parent Process ID(6339)가 SQL*Plus 세션의 프로세스 ID임을 알 수 있습니다.

프로세스 이름("oracleDBA102 (DESCRIPTION=(LOCAL=YES) (ADDRESS=(PROTOCOL=beq)))")에서도 몇 가지 정보를 얻을 수 있습니다. 먼저 “LOCAL=YES”는 이 프로세스가 로컬(데이터베이스가 실행되는 서버)에서 실행 중인 다른 프로세스에 의해 시작되었음을 의미합니다. 또 “PROTOCOL=beq”는 “bequeath” 연결을 통해 연결이 설정되었음을 의미합니다.

다이내믹 뷰를 통해 서버 프로세스에 대한 정보를 확인할 수도 있습니다.

select spid
from v$session s, v$process p
where s.sid = (select sid from v$mystat where rownum <2)
and p.addr = s.paddr;

위 쿼리를 실행하면 서버 프로세스의 프로세스 ID가 반환됩니다. 클라이언트 프로세스가 다른 서버에서 실행 중이라면(예: 사용자의 노트북에서SQL*Plus으로 데이터베이스에 접속한 경우), 이 쿼리가 프로세스 ID를 확인하기 위한 유일한 방법입니다.

이제 사용자가 다른 방법을 사용하여 접근하는 경우, 서버에 직접 접속하는 대신 TNS 문자열을 사용하는 경우를 가정해 봅시다. oradba 서버의 TNS 문자열은 아래와 같습니다.

DBA102 =
 (DESCRIPTION =
   (ADDRESS_LIST =
     (ADDRESS = (PROTOCOL = TCP)(HOST = oradba)(PORT = 1521))
   )
   (CONNECT_DATA =
     (SERVER = DEDICATED)
     (SERVICE_NAME = DBA102)
   )
 )

이제 사용자가 아래와 같은 방법으로 oradba 서버에 연결합니다:

sqlplus arup/arup@dba102

다이내믹 뷰에서 프로세스 ID를 확인해 봅시다:

SQL> select spid
 2  from v$session s, v$process p
 3  where s.sid = (select sid from v$mystat where rownum <2)
 4  and p.addr = s.paddr
 5  /

SPID
------------
6428

프로세스 ID는 6428입니다. 프로세스 ID를 기준으로 서버의 프로세스를 조회해 보겠습니다:

$ ps -aef|grep sqlplus | grep -v grep
oracle    6426  6185  0 13:20 pts/0    00:00:00 sqlplus

이제 데이터베이스 서버의 서버 프로세스를 검색하면 아래와 같은 결과를 얻습니다:

$ ps -aef|grep 6426 | grep -v grep
oracle 6426 6185 0 13:20 pts/0 00:00:00 sqlplus

....서버 프로세스가 확인되지 않았습니다. 사용자 프로세스 6426의 자식 프로세스가 존재하지 않습니다. 하지만 다이내믹 성능 뷰를 통해 서버 프로세스의 ID가 6428이라는 사실은 이미 확인했습니다. 그렇다면 프로세스 ID 6428의 부모 프로세스는 누구일까요?

$ ps -aef|grep 6428 | grep -v grep
oracle    6428     1  0 13:20 ? 00:00:00 oracleDBA102 (LOCAL=NO) 

부모 프로세스는 “1”입니다. 왜 6426이 아니고 1일까요?

그 답을 얻으려면, 먼저 오라클 서버 프로세스가 생성되는 방법을 이해해야 합니다. 사용자가 TNS 문자열을 사용하지 않은 첫 번째 예의 경우, 연결은 리스너를 거치지 않고 데이터베이스로 직접 전달되었습니다. 데이터베이스는 사용자 프로세스를 생성하고 프로세스의 컨트롤을 프로세스 소유자에게 넘겨 줍니다. 이러한 절차를 “bequeathing”이라 부르며, “bequeath” 프로세스라는 용어 역시 여기에서 유래합니다.

두 번째의 예에서는 사용자가 여전히 동일한 서버에 위치하지만 이번에는 리스너를 통해 연결을 시도합니다. 리스너는 사용자를 위한 프로세스를 생성하며 이 과정을 “forking”이라 부릅니다. 사용자 프로세스가 랩탑과 같은 다른 머신에서 실행되는 경우에도, 연결은 리스너로 전달되고 리스너가 프로세스를 생성하게 됩니다. 이 경우 프로세스가 원격 서버에 의해 생성되므로 프로세스 이름에 “LOCAL=NO”라는 문자열이 포함됩니다. 설사 SQL*Plus가 같은 서버에서 실행되고 있다 하더라도, “bequeath” 연결이 아닌 이상 “LOCAL=NO”로 표시됩니다.

(참고: 일부 운영체제에서는 “bequeath” 프로세스로 생성된 서버 프로세스의 부모 ID를 확인할 수 없을 수도 있습니다. 또 “bequeath” 연결임에도 불구하고 부모 ID가 “1”로 표시되는 경우도 있습니다. 따라서 서버 프로세스의 타입을 확인할 때에는 부모 ID가 아닌 프로세스 이름을 참고해야 합니다.)

이제 “two-task” 모델에 대해 이해하셨으리라 생각합니다. 그렇다면 지금까지 설명한 내용의 가장 중요한 교훈은 무엇일까요? 바로 SQL*Plus와 같은 클라이언트 프로세스가 아닌 데이터베이스가 서버 프로세스를 생성하고 실행한다는 사실입니다. “oracle” 또는 “oracle.exe” 실행 파일은 서버 프로세스에 의해 실행되며, 따라서 오라클 소프트웨어의 소유자(“orasoft”)만이 해당 바이너리를 실행할 권한을 가져야 합니다. 그렇다면 왜 다른 사용자들에게 실행 권한을 부여할 필요가 있는 것일까요?

간단히 답부터 말한다면, 그럴 필요가 없습니다. 아래와 같이 실행하여 불필요한 권한을 제거할 수 있습니다:

$ chmod 4700 $ORACLE_HOME/bin/oracle

명령을 실행한 후의 권한 설정이 아래와 같습니다.

-rws------ 1 orasoft oinstall 248754168 Oct 8 07:11 oracle

여기서는 SUID 비트를 이용하여 설정을 변경하는 방법을 사용하기로 합니다. 이 경우 (소유자의 rws 권한에 명시된 대로) SUID 비트는 ON으로 설정되어 있습니다.

전략
오라클 소프트웨어 소유자(“orasoft”) 이외의 어떤 사용자도 오라클 실행 파일을 실행할 필요가 없으므로, SUID 비트를 제거하고 바이너리의 소유자에 의해서만 접근이 가능하도록 설정해야 합니다.
$ chmod 0700 $ORACLE_HOME/bin/oracle

변경된 설정이 아래와 같습니다:

-rwx------ 1 orasoft oinstall 248754168 Oct 8 07:11 oracle

주의 사항
이것은 매우 중요한 변경 사항이며, 따라서 그 영향을 이해할 필요가 있습니다. 오라클 소프트웨어의 소유자가 아닌 사용자가 로컬 연결을 사용하여 서버에 접근하는 경우, “oracle” 실행 파일은 “orasoft” 사용자에 의해 실행됩니다. 이것은 매우 중요한 사실입니다. 서버 프로세스가 (“orasoft”가 소유한) 데이터 파일을 열게 되므로, 프로세스가 “orasoft”의 이름으로 실행되거나 사용자가 데이터 파일을 열 수 있는 권한을 가져야 합니다.

한 예로, UNIX 사용자 “ananda”가 데이터베이스 서버에 로그인한 후 로컬 연결을 시도하는 경우를 가정해 봅시다:

$ sqlplus arup/arup

사용자는 아래와 같은 에러를 확인하게 됩니다.

ERROR:
ORA-12546: TNS:permission denied

Enter user-name:

그 이유는 아주 간단합니다. “oracle” 파일의 SUID 권한을 제거했기 때문입니다. 사용자가 로컬 연결을 시도한다는 것은, 결국 사용자가 “oracle” 실행 파일을 실행함을 의미합니다. 하지만 SUID가 설정되어 있지 않으므로 “orasoft”가 아닌 “ananda”의 이름으로 실행됩니다. ananda가 이 파일을 실행할 권한이 없으므로 ORA-12546 에러가 발생하는 것입니다.

그렇다면 어떻게 하면 ananda가 데이터베이스에 연결할 수 있을까요? 두 가지 대안이 있습니다. 그 하나는 모든 사용자 프로세스가 데이터베이스 서버와 다른 서버에서 실행되도록 함으로써 “bequeath” 연결이 생성되지 않도록 하는 것입니다. 로컬로 실행되지 않는 모든 연결은 리스너를 거치며, 리스너가 사용자를 위한 서버 프로세스를 대신 생성해 주므로, 서버 프로세스는 클라이언트 프로세스를 실행하는 사용자가 아닌 “orasoft”(오라클 소프트웨어 소유자)에 의해 실행됩니다. 따라서 별도로 권한을 설정해 줄 필요가 없습니다.

또 다른 대안으로, 데이터베이스 서버에서 사용자 프로세스를 로컬 실행해야 하는 경우라면 아래와 같은 방법으로 리스너를 거칠 수 있습니다.

$ sqlplus arup/arup@dba102

위 명령은 다른 서버에서 사용자가 접근하는 것과 같은 효과를 갖습니다. 따라서 오라클 소프트웨어를 소유한 사용자(orasoft)만이 “bequeath” 연결을 통해 데이터베이스에 연결함을 보장할 수 있습니다.

별도의 운영체제 ID를 갖는 DBA 계정은 (설사 dba 그룹에 포함되어 있더라도) “connect / as sysdba” 커맨드를 사용해서 데이터베이스의 시작/셧다운을 수행할 수 없습니다. 이 경우 아래와 같은 커맨드를 사용해야 합니다.

$ sqlplus /nolog
SQL> connect sys/Password_of_SYS@dba102 as sysdba

이 경우 SYS 암호를 사용하게 되며, 이는 “/ as sysdba”보다 더 효과적인 방법입니다. 하지만 좀 더 나은 방법은 각각의 DBA를 위해 별도의 Oracle UserID를 생성하는 것입니다.

connect ANANDA/Password_of_ANANDA@dba102 as sysdba

해커들은 임의의 계정을 이용하여 데이터베이스에 강제 침입하는 방법을 즐겨 사용하곤 합니다(이때 자주 사용되는 계정이 “nobody”입니다). 해커가 데이터베이스에 진입하지 못한 경우라도 오라클 실행 파일에 버퍼 오버플로우를 발생시켜 DoS 공격을 시도하는 것이 가능합니다. 파일의 실행 권한을 제거하는 방법으로, 공격의 피해를 극적으로 감소시킬 수 있습니다. 이렇게 하더라도, 합법적인 사용자는 아무런 제약을 받지 않습니다. 대부분의 사용자는 리스너를 사용하여 데이터베이스에 연결하므로 설정 변화에 아무런 영향을 받지 않습니다.

실행 계획
준비 작업
“bequeath” 연결을 사용하여 시스템에 접근하는 사용자가 있는지 확인합니다. 두 가지 방법이 가능합니다:
  1. 사용자들에게 물어봅니다
  2. 서버의 프로세스를 검색하여 SQL*Plus 등을 이용한 연결이 있는지 확인합니다.
  3. V$SESSION의 MACHINE 컬럼을 확인합니다.
select program
from v$session
where machine = '<machine_name>';

의심스러운 문제가 발견되었다면, 감사(audit) 기능을 활성화하여 실행 중인 프로그램을 확인하고 그 결과를 캡처합니다 (여기에 대해서는 다음 단계에서 상세하게 설명합니다)

Action
IF <서버로부터 연결된 프로그램이 없는 경우>, THEN
  오라클 실행 파일의 권한을 변경
chmod 0700 $ORACLE_HOME/oracle
ELSIF some program connects from the server
  연결을 UserID/Password에서 UserID/Password@Connect_String으로 변경
END IF
IF <”sqlplus / as sysdba”를 이용하여 쉘로부터 자주 연결하는 경우> THEN
  DBAUser/Password@Connect_String을 사용하도록 변경
END IF


1.3 다른 실행 파일의 보안

배경 정보

$ORACLE_HOME/bin 디렉토리의 다른 실행 파일을 살펴 봅시다. Sqlplus, Isnrctl(리스너 시작을 위한 유틸리티) 등 몇 가지는 눈에 익기도 하지만, 몇 가지는 처음 보는 것일 수도 있습니다.

tnslsnr(Listener 프로세스가 실행하는 유틸리티), dbsnmp(Oracle Intelligent Agent에 의해 사용)과 같은 일부 파일은 엔드 유저가 직접 접근하지 않습니다. 이러한 실행 파일의 보안을 위해 적절한 조치를 취할 필요가 있습니다.

앞에서 설명한 것처럼 SUID 비트가 설정되어 있으면, 파일을 실제로 누가 실행하는가에 관계없이 파일이 소유자의 권한으로 실행됩니다. 또 SUID 설정은 보안상 매우 위험할 수 있으며 따라서 가능한 한 사용을 자제해야 합니다.

SUID가 설정된 몇 가지 실행 파일이 존재합니다. 한 번 찾아 보기로 합시다.

$ cd $ORACLE_HOME
$ find . -type f \( -perm -2000 -o -perm -4000 \) -exec ls -l {} \; 

Oracle Database10g Release 1 이후 버전에서는 아래와 같은 실행 파일이 반환됩니다:

-rwsr-s--x    1 orasoft  dba      93300507 Jul 22 11:20 ./bin/oracleO
-r-sr-s---    1 root     dba             0 Jul  1 23:15 ./bin/oradism
-rwsr-s--x    1 orasoft  dba         94492 Jul 22 11:22 ./bin/emtgtctl2
-rwsr-s---    1 root     dba         18944 Jul 22 11:22 ./bin/nmb
-rwsr-s---    1 root     dba         20110 Jul 22 11:22 ./bin/nmo
-r-sr-sr-x    1 nobody   nobody      58302 Jul 22 11:23 ./bin/extjob

각각의 파일에 대해 알아 보겠습니다:

프로그램

설명

./bin/oracleO

“oracle” 실행 파일의 복사본입니다. relink 커맨드를 사용하여 오라클 실행파일을 재컴파일한 경우, 기존의 파일은 oracle0라는 이름으로 저장됩니다. 이것은 잠재적인 보안 위협으로 작용할 수 있습니다. 대부분의 DBA들이 oracle0의 존재를 간과하고 있기 때문에 해커에게는 손쉬운 진입 경로로 활용됩니다. 따라서 권한을 제거하는 작업을 수행해 주어야 합니다. 이 파일에 대해서는 권한을 완전히 제거하는 것이 바람직합니다:

$ chmod 0000 oracleO

이제 권한 설정 상태를 확인해 봅시다:

$ ls -l oracleO
---------- 1 orasoft oinstall 248823320 Sep 15 13:27 oracleO

./bin/oradism

Dynamic Intimate Shared Memory를 위해 사용됩니다. 플랫폼에 따라 사용될 수도 있고 사용되지 않을 수 있으며, 경우에 따라 표시되지 않을 수도 있습니다. 이 파일의 권한은 변경하지 않습니다.

./bin/emtgtctl2

Enterprise Manager Agent를 위해 사용됩니다. 이 실행 파일은 SUID가 설정되어 있을 필요가 없습니다(그 이유는 “oracle” 실행 파일의 경우와 같습니다). 아래와 같이 권한을 제거합니다.

$ chmod 0700 emtgtctl2

./bin/nmb

Oracle 10g Grid Control 에이전트가 타겟 서버의 성능 통계를 수집하는데 사용합니다. 이 파일의 권한은 변경하지 않습니다.

./bin/nmo

Oracle 10g Grid Control 에이전트가 타겟 서버의 성능 통계를 수집하는데 사용합니다. 이 파일의 권한은 변경하지 않습니다.

./bin/extjob

EXTJOB (External Jobs; Enterprise Manager 내에서 OS 프로그램을 실행하기 위한 기능)을 위한 실행 파일입니다. 이 파일은 조심할 필요가 있습니다. 외부 작업을 실행할 필요가 있는지 먼저 자문해 보십시오. 필요가 없다면 이 실행 파일 또한 불필요하며, 따라서 권한과 소유권을 변경해 줄 수 있습니다. 사용자는 오라클 소프트웨어 소유자(orasoft)로 권한은 “rwx------”로 설정해 줍니다.

$ chown orasoft:oinstall extjob
$ chmod 0700 extjob

extjobO이라는 이름의 또 다른 프로그램이 존재할 수도 있습니다(동일한 프로그램의 이전 컴파일된 버전입니다). 이 파일의 권한 역시 변경해 줍니다.

$ chown orasoft:oinstall extjobO
$ chmod 0000 extjobO

Oracle9i Database Release 2에서는 ./bin/dbsnmp 파일(Oracle Intelligent Agent 실행 파일)이 존재합니다. 이 파일의 권한은 아래와 같이 설정되어 있습니다:

-rwsr-s--- 1 root dba 2986836 Jan 26 2005 dbsnmp

이 파일은 루트 권한이 있어야만 정상적으로 실행이 가능하며, 따라서 SUID 비트가 설정되어 있어야 합니다. 하지만 이 파일이 루트 계정에 의해 소유되어 있기 때문에, 해커들이 루트 권한을 획득하는 방법으로 활용되기도 합니다. 가장 좋은 방법은 파일을 제거하거나 오라클 소프트웨어의 소유자가 이 파일을 소유하도록 하고 권한을 700으로 설정하는 것입니다. 이로 인해 일부 기능이 제약될 수도 있지만, 그 정도의 위험을 감수할 만한 가치가 있습니다.

그 밖에 고려해야 할 실행파일로 tnslsnr(Oracle Net Listener)이 있습니다. 이에 관련하여 두 가지 실행 파일이 존재합니다:

tnslsnr – 리스너의 실행 파일

lsnrctl – 리스너의 관리(시작, 중단 등)를 위한 유틸리티.

설정된 권한을 조회해 봅시다:

$ ls -l *lsnr*
-rwxr-x--x   1 orasoft    oinstall    214720 Oct 25 01:23 lsnrctl
-rwxr-xr-x   1 orasoft    oinstall    214720 Oct  1 18:50 lsnrctl0
-rwxr-x--x   1 orasoft    oinstall   1118816 Oct 25 01:23 tnslsnr
-rwxr-xr-x   1 orasoft    oinstall   1118816 Oct  1 18:50 tnslsnr0

두 파일에 대해 모든 사용자에게 실행 권한이 부여되어 있습니다. oracleO 실행파일과 마찬가지로, 오라클 소프트웨어의 재링크(relink)를 통해 새로운 tnslsnr 파일이 생성되면 기존의 tnslsnr 파일은 tnslsnr0로 이름이 변경됩니다. tnslsnr0 파일은 프로세스를 롤백 해야 하는 경우에 기존의 실행 파일을 새로운 파일에 덮어쓰기 할 때 사용됩니다. tnslsnr0 파일은 기존의 tnslsnr 파일과 동일한 기능을 수행할 수 있습니다. lsnrctl0 파일 역시 마찬가지입니다.

전략
각 실행 파일의 목적을 이해했다면, 이제 데이터베이스 인프라스트럭처의 보안을 구현하는 방법에 대해 알아봅시다. 대부분의 전략은 위 섹션에서 이미 충분히 설명되었습니다. 결론적으로, DBA가 취해야 할 전략이 아래와 같습니다:

  1. 불필요한 파일의 모든 권한을 제거합니다(예: lsnrctl0).
  2. 실행 파일의 권한을 오라클 소프트웨어로만 제한합니다.
  3. 오라클 소프트웨어 소유자가 프로세스를 시작하는 경우 SUID 비트를 제거합니다.

리스너에 관련된 파일의 권한은 아래와 같은 방법으로 변경합니다.

$ chmod 700 lsnrctl tnslsnr
$ chmod 000 lsnrctl0

그 결과를 확인해 봅시다.

$ ls -l *lsnr*
-rwx------   1 orasoft    oinstall    214720 Oct 25 01:23 lsnrctl
----------   1 orasoft    oinstall    214720 Oct  1 18:50 lsnrctl0
-rwx------   1 orasoft    oinstall   1118816 Oct 25 01:23 tnslsnr
----------   1 orasoft    oinstall   1118816 Oct  1 18:50 tnslsnr0

주의 사항
몇 가지 주의해야 할 사항이 다음과 같습니다:

  • racle 실행 파일을 변경하는 것은 데이터베이스 운영에 아무런 영향을 미치지 않습니다. “oracle” 실행파일의 손상으로 인한 문제를 겪는 경우, “oracleO” 파일의 이름을 “oracle”로 변경하는 방법으로 문제를 해결하는 것이 가능합니다. 이때 권한을 700으로 재설정하도록 주의합니다. lsnrctl0, tnslsnrctl0 파일의 경우도 마찬가지 입니다.


  • 오라클 소프트웨어 소유자의 사용자 아이디를 Enterprise Manager 운영체제 로그인 계정으로 사용하는 경우, emtgtctl2 파일의 권한을 변경해도 아무런 영향이 없습니다. (orasoft가 아닌) 다른 사용자 아이디를 사용하는 경우에는 SUID와 권한을 기존의 값으로 재설정해 주어야 합니다.


  • dbnsmp 실행파일은 until Oracle9i Database Release 2와 그 이전 버전에서만 Oracle Enterprise Manager Intelligent Agent에 의해 사용됩니다. 오라클 소프트웨어 소유자의 사용자 아이디를 운영체제 로그인 계정으로 사용하는 경우에는, 이 파일의 실행 권한을 변경해 주어도 아무런 영향이 없습니다. 다른 사용자 아이디를 사용하는 경우라면 실행 권한을 이전의 값으로 재설정해 주어야 합니다.
실행 계획
  1. oracleO, tnslsnr0, lsnrctl0의 실행 권한을 0000으로 변경합니다.
  2. tnslsnr과 lsnrctl의 권한을 0700으로 변경합니다.
  3. Enterprise Manager에서 외부 작업(external job)을 사용하고 있습니까?


    IF <그렇지 않은 경우> THEN <extjob의 권한을 0000으로 변경합니다.>
    ELSE
      extjob의 권한을 0700으로 수정하고 소유자와 그룹을 각각 orasoft와 oinstall(또는 오라클 소프트웨어 소유자의 사용자 아이디와 그룹)으로 변경합니다.
    END IF
  4.  
    IF Oracle9i Database를 사용하는 경우
      Oracle Intelligent Agent를 사용 중입니까?
    IF <그렇지 않은 경우> THEN
      dbsnmp의 오너십을 orasoft로 변경합니다.
    권한을 0700으로 변경합니다.
    ELSE
      변경 작업이 불필요합니다.
    END IF


1.4 umask의 사용

배경 정보
여러분도 잘 알고 계시다시피, UNIX/Linux 환경에서는 chmod 커맨드를 이용하여 실행 권한을 변경해 줄 수 있습니다. 하지만 chmod는 이미 존재하는 파일에 대해서만 동작합니다. 그렇다면 차후에 생성될 파일에 대해 동일한 권한이 설정됨을 어떻게 보장할 수 있을까요?

예를 들어 디렉토리의 모든 파일이 r--r--r—(444) 권한을 갖도록 설정해야 하는 경우를 가정해 봅시다. 아래와 같은 방법을 사용하면 됩니다:

$ chmod 444 *

이제 해당 디렉토리에 내용이 없는 파일 하나를 생성하고 그 권한을 확인합니다.

$ touch a_file.txt
$ ls -l a_file.txt
-rw-r--r--   1 orasoft    dba        0 Oct 21 13:44 a_file.txt

소유자에 대해 읽기/쓰기, 그룹에 대해 읽기, 그리고 다른 사용자에 대해 읽기 (644) 권한이 설정되어 있습니다. 여러분이 기대했던 444와는 다른 결과입니다. 그 이유가 무엇일까요?

새로 생성되는 파일의 실행 권한은 umask라는 이름의 매개변수에 의해 결정됩니다. umask는 모든 권한이 부여된 chmod 값(777)에서 새로운 파일의 chmod 값을 뺀 값을 저장하고 있습니다. 예를 들어 umask를 777로 설정하면, 새로운 파일의 권한은 777에서 777을 뺀 값, 즉 000이 됩니다. 그 실행 예가 아래와 같습니다:

$ umask 777
$ touch b_file.txt
$ ls -l ?_file.txt
-rw-r--r--   1 oracrmp    dba              0 Oct 21 13:44 a_file.txt
----------   1 oracrmp    dba              0 Oct 21 13:53 b_file.txt

b_file.txt 파일의 권한이 000으로 설정되어 있음을 확인하시기 바랍니다. 하지만 앞에서 생성된 a_file.txt의 권한은 변경되지 않았습니다. umask의 값(777)은 새로운 파일에만 영향을 미친다는 사실을 알 수 있습니다.

umask는 오라클이 생성하는 파일의 권한을 제어하기 위한 매우 효과적이고 강력한 도구입니다.

전략
오라클 소프트웨어 소유자의 umask는 022로 설정되어야 합니다. 즉, 소유자에게는 Read+Write 권한이, 다른 사용자에게는 Read 권한이 할당되어야 합니다. 이 설정을 사용자의 로그인 프로파일에 저장하여 언제든 실행될 수 있도록 할 수 있습니다.

오라클이 사용하는 파일에는 데이터 파일, 리두 로그 파일, 트레이스 파일 등 여러 가지 종류가 있습니다. 데이터파일은 미리 생성되며 그 권한을 쉽게 변경할 수 있지만, 트레이스 파일은 런타임에 생성됩니다. 따라서 umask를 이용하여 파일이 외부 사용자에게 노출되지 않도록 할 필요가 있습니다. 트레이스 파일은 해커에 의해 악용될 수 있는 민감한 정보를 포함하고 있습니다. 예를 들어, 데이터 파일을 복제하고 별도의 서버에 마운트하는 방법으로 정보를 유출하는 것이 이론적으로 가능합니다.

각 디렉토리의 umask를 아래와 같이 설정합니다:

디렉토리

설명

umask

초기화 매개변수
background_dump_dest에 지정된 디렉토리

이 위치에서 일부 트레이스 파일과 데이터베이스 경고 로그가 생성됩니다. 권한은 rw------- (오라클 소프트웨어 소유자에게만 Read+Write 권한 부여)로 설정되어야 합니다.

0177

초기화 매개변수
user_dump_dest에 지정된 디렉토리

이 위치에서 트레이스 파일이 생성됩니다. 권한은 위와 동일한 방법으로 설정되어야 합니다.

0177

$ORACLE_HOME/rdbms/log

이 위치에서 일부 데이터베이스 로그 파일이 생성됩니다. 권한은 위와 동일한 방법으로 설정되어야 합니다.

0177

$ORACLE_HOME/rdbms/audit

audit_file_dest 초기화 매개변수를 별도로 설정하지 않은 이상, 데이터베이스 감사 로그(audit trail)가 이곳에 저장됩니다. 권한은 위와 동일한 방법으로 설정되어야 합니다. DB 감사 로그를 별도로 관리하는 경우에도, SYSDBA 연결, 데이터베이스 스타트업/셧다운과 같은 일부 이벤트는 항상 이곳에 기록됩니다.

0177

초기화 매개변수
audit_file_dest에 지정된 디렉토리

audit_file_dest 초기화 매개변수를 별도로 설정한 경우, 데이터베이스 감사 로그(audit trail)가 이곳에 저장됩니다. 권한은 위와 동일한 방법으로 설정되어야 합니다.

0177

주의 사항
위와 같은 방법으로 umask를 적용한 경우, user_dump_dest 디렉토리에 생성된 세션 트레이스 파일에 개발자들이 접근할 수 없는 상황이 발생할 수 있습니다. 따라서 이 디렉토리에 대해서만큼은 예외적인 룰을 정의할 필요가 있습니다.

실행 계획
  • background_dump_dest의 umask를 0177로 변경합니다.
  • $ORACLE_HOME/rdbms/log의 umask를 0177로 변경합니다.
  • $ORACLE_HOME/rdbms/audit의 umask를 0177로 변경합니다.
  • audit_file_dest의 umask를 0177로 변경합니다.
  • (선택 사항) user_dump_dest의 umask를 0177로 변경합니다.


1.5 SYSDBA 로그인의 제한

배경 정보 “dba” 그룹에 포함된 UNIX/Linux 사용자는 아래와 같은 명령을 이용하여 SYSDBA 권한으로 로그인할 수 있습니다:

sqlplus / as sysdba

DBA가 SYS 패스워드를 기억하거나 입력할 필요가 없다는 편의성 때문에 이러한 방법이 자주 사용되곤 합니다. 하지만 이로 인한 취약점 또한 존재합니다. dba 그룹 멤버로 로그인할 수 있는 모든 사용자는 SYS 권한으로 데이터베이스에 로그인할 수 있습니다. SYS에 아무리 강력한 암호 체계를 적용해도 이는 무용지물과 다름없습니다. SYS 계정의 강력한 권한을 보호하려면 dba 그룹이 SYS 계정으로 로그인하면서 SYS 암호를 반드시 입력하도록 하는 것이 좋습니다. 이런 방법으로 외부 침입을 완전히 차단할 수는 없겠지만, 그 위험을 상당 수준 줄일 수 있습니다.

전략
이 프로세스는 SQLNET.ORA 파일의 SQLNET.AUTHENTICATION_SERVICES 매개변수에 의해 통제됩니다. 이 매개변수가 NONE으로 설정되었다면, SYSDBA 역할의 자동 로그인 기능은 비활성화됩니다. 자동 로그인의 비활성화를 위해 $ORACLE_HOME/network/admin 디렉토리에 위치한 SQLNET.ORA 파일에 아래 라인을 삽입해 줍니다.

SQLNET.AUTHENTICATION_SERVICES=(NONE)

이제 dba 그룹에 포함된 사용자가 아래와 같은 방법으로 로그인을 시도하면:

$ sqlplus / as sysdba

다음과 같은 에러를 확인하게 됩니다:

ERROR:
ORA-01031: insufficient privileges 

성공적으로 연결하기 위햇는 SYS 패스워드를 함께 입력해야 합니다:

$ sqlplus /nolog
SQL> connect sys/oracle as sysdba

이와 같은 방법으로 SYS 패스워드를 알지 못하는 해커가 dba 계정을 이용하여 접근을 시도하는 것을 차단할 수 있습니다.

주의 사항
위에서 분명히 확인할 수 있는 것처럼, SYS 패스워드를 명시적으로 입력해야 한다는 사실이 중요합니다. 따라서 SYS 계정으로 연결하는 스크립트에 변경 사항을 적용해 주어야 합니다.

SYS 패스워드를 분실하더라도 걱정할 필요는 없습니다. SQLNET.ORA 파일의 라인을 주석 처리하고 이전과 같은 방법(/ as sysdba)으로 로그인할 수 있습니다.

실행 계획
IF <스크립트에서 SYS 계정을 이용하여 연결하는 경우> THEN
  ““/ as sysdba”를 “ sys/<SysPassword> as sysdba”로 변경합니다.
SQLNET.ORA 파일에 “SQLNET.AUTHENTICATION_SERVICES=(NONE)” 설정을 추가합니다.
ELSE
  변경 작업이 불필요합니다.
END IF


1.6 Listener 패스워드의 생성

배경 정보
해커들이 가장 즐겨 사용하는 트릭 중 하나가 대량의 텍스트를 리스너로 전달하여 실행을 강제 종료하도록 유도하는 것입니다. 이 경우 데이터베이스는 여전히 실행 중이지만 리스너가 다운되기 때문에 새로운 연결을 생성할 수 없게 되므로, “서비스 거부(denial of service)” 공격이 가능합니다.

해커는 이를 위해 리스너의 속성 변경을 시도할 수 있습니다. 이를 위해 services 커맨드를 이용하여 리스너에 의해 처리되는 서비스의 목록을 조회하는 방법이 자주 사용됩니다. 아래 실행 예에서 볼 수 있는 것처럼 해커에게 필요한 충분한 수준의 정보가 제공되고 있음을 확인할 수 있습니다.

LSNRCTL> set displaymode verbose
LSNRCTL> services
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)
(HOST=prolin1.proligence.com)(PORT=1521)(IP=FIRST)))
Services Summary...
Service "PROPRD" has 1 instance(s).
  Instance "PROPRD1", status READY, has 1 handler(s) for this
service...
    Handler(s):
      "DEDICATED" established:0 refused:0 state:ready
         LOCAL SERVER
(ADDRESS=(PROTOCOL=BEQ)(PROGRAM=/u01/oracle/products/10.1/db1/bin/ora
cle)(ARGV0=oraclePROPRD11)(ARGS='(LOCAL=NO)')(ENVS='_=/u01/oracle/pro
ducts/10.1/db1/bin/racgmain,_USR_ORA_CONNECT_STR=/ as
sysdba,_CAA_CHECK_INTERVAL=600,SHLIB_PATH=/u01/oracle/products/10.1/d
b1/lib32:/u01/oracrs/10gr1crs/lib32:/opt/nmapi/nmapi2/lib/hpux32:,_CA
A_ACTIVE_PLACEMENT=0,PATH=,_USR_ORA_ALERT_NAME=,_USR_ORA_IF=,_CAA_OPT
IONAL_RESOURCES=,_USR_ORA_START_TIMEOUT=0,ORACLE_BASE=/u01/oracle/pro
ducts/10.1/db2,_USR_ORA_DISCONNECT=false,_CAA_SCRIPT_TIMEOUT=600,_CAA
_UPTIME_THRESHOLD=7d,_USR_ORA_STOP_TIMEOUT=0,_CAA_FAILOVER_DELAY=0,_U
SR_ORA_PRECONNECT=none,_USR_ORA_FLAGS=,_CAA_TYPE=application,_USR_ORA
_INST_NOT_SHUTDOWN=,_CAA_REASON=boot,INIT_STATE=3,_USR_ORA_OPEN_MODE=
,_CAA_STATE=:OFFLINE,,_CAA_RESTART_ATTEMPTS=5,_CAA_ACTION_SCRIPT=/u01
/oracle/products/10.1/db1/bin/racgwrap,_CAA_DESCRIPTION=CRS
application for
Instance,_CAA_HOSTING_MEMBERS=prolin1,ORA_RACG_EXEC_ENV=LD_LIBRARY_PA
TH=/u01/oracle/products/10.1/db1/lib:/u01/oracrs/10gr1crs/lib:/opt/nm
api/nmapi2/lib/hpux64:/usr/lib:,_CAA_CLIENT_LOCALE=,_CAA_NAME=ora.PRO
PRD1.PROPRD11.inst,ORA_CRS_HOME=/u01/oracrs/10gr1crs,_CAA_AUTO_START=
1,_CAA_TARGET=:ONLINE,,_USR_ORA_PFILE=,_USR_ORA_OPI=false,_USR_ORA_CH
ECK_TIMEOUT=0,_CAA_PLACEMENT=restricted,_USR_ORA_LANG=,LD_LIBRARY_PAT
H=/u01/oracle/products/10.1/db1/lib:/u01/oracrs/10gr1crs/lib:/opt/nma
pi/nmapi2/lib/hpux64:/usr/lib:,_CAA_REQUIRED_RESOURCES=ora.prolin1.vi
p,_CAA_FAILURE_THRESHOLD=0,ORACLE_HOME=/u01/oracle/products/10.1/db1,
_USR_ORA_SRV=,PWD=/u01/oracrs/10gr1crs/bin,_USR_ORA_VIP=,_USR_ORA_STO
P_MODE=immediate,_CAA_FAILURE_INTERVAL=0,_USR_ORA_NETMASK=,_USR_ORA_D
EBUG=0,ORACLE_SID=PROPRD1,ORA_NET2_DESC=9,12,ORACLE_SPAWNED_PROCESS=1
')(ENV_POLICY=NONE))
또 다른 해킹 유형으로 리스너를 셧다운하는 방법이 있습니다. 새로운 연결은 거부되며, 따라서 실질적인 서비스 거부 공격이 가능합니다.

또는 다른 서버에 먼저 침입한 후 리스너의 원격 관리 기능을 이용하여 리스너를 원격에서 종료하는 방법이 가능합니다.

이러한 위협으로부터 데이터베이스를 보호하려면 어떻게 해야 할까요?

전략
최선의 대안은 tnslsnr, lsnrctl 실행 파일의 소유자를 제외한 모든 권한을 제거하는 것입니다. (이 방법은 앞 섹션에서 설명한 바 있습니다.) 이러한 방법으로 오라클 소프트웨어의 소유자를 제외한 어느 누구도 리스너를 시작 또는 종료할 수 없도록 통제할 수 있습니다. 실행 파일의 권한은 아래와 같이 설정됩니다.

-rwx------   1 orasoft    oinstall    214720 Oct 25 01:23 lsnrctl
-rwx------   1 orasoft    oinstall   1118816 Oct 25 01:23 tnslsnr

경우에 따라 리스너의 시작/종료 권한을 다른 사용자에게 허용해야 할 수도 있습니다. 이러한 경우라면 아래와 같이 권한을 변경해 주어야 합니다.

$ chmod 0711 lsnrctl

하지만 이와 같은 경우라 하더라도 패스워드 정책을 통해 불법적인 침입을 차단할 수 있어야 합니다. 패스워드를 설정하면 (HELP와 같은 무해한 명령을 제외한) 모든 커맨드가 비활성화됩니다.

패스워드를 설정하는 방법은 버전에 관계없이 동일합니다. 하지만 그 적용 메커니즘은 버전에 따라 다릅니다:

  • Oracle9i Database Release 2 및 이전 버전에서는 모든 사용자가 패스워드를 입력해야 합니다.
  • Oracle Database 10g Release 1 및 이후 버전에서는 데이터베이스 소프트웨어를 소유한 OS 사용자는 패스워드를 입력할 필요가 없습니다. 다른 모든 사용자는 패스워드를 필요로 합니다.

패스워드의 설정 방법이 아래와 같습니다:

$ lsnrctl

LSNRCTL> change_password
Old password: <OldPassword> Not displayed
New password: <NewPassword> Not displayed
Reenter new password: <NewPassword> Not displayed
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=prolin1)(PORT=1521)(IP=FIRST)))
Password changed for LISTENER
The command completed successfully

패스워드를 처음으로 설정하는 경우, “Old Password”를 묻는 프롬프트에서 그냥 ENTER 키를 눌러도 됩니다. 변경 사항을 적용한 뒤 그 결과를 매개변수 파일에 저장합니다:

LSNRCTL> save_config

위 커맨드는 패스워드를 암호화하여 리스너 매개변수 파일에 저장합니다. 그 내용은 나중에 확인이 가능합니다:

#----ADDED BY TNSLSNR 24-OCT-2005 17:02:28---
PASSWORDS_LISTENER_ODSSDB01 = 75CD180DE6C75466
#--------------------------------------------

이제 커맨드를 사용하려면 패스워드를 입력해야 합니다 (Oracle Database 10g 및 이후 버전에서는 소프트웨어를 소유한 OS 사용자는 패스워드를 입력할 필요가 없습니다.) LSNRCTL> services Connecting to (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521)) TNS-01169: The listener has not recognized the password

패스워드를 입력하는 방법이 아래와 같습니다:

LSNRCTL> set password mypassword
The command completed successfully
LSNRCTL> status
Connecting to (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
...

잘못된 패스워드가 입력되면 아래와 같은 에러가 뜹니다.

TNS-01169: The listener has not recognized the password.

패스워드를 입력하지 않고 명령을 실행하면 아래와 같은 에러가 뜹니다.

TNS-01190: The user is not authorized to execute the requested listener command

패스워드가 적용되었는지 확인하기 위해서는 아래와 같이 실행하여 리스너의 STATUS 설정을 조회합니다:

$ lsnrctl status

출력 결과는 버전에 따라 다릅니다. Oracle9i Database 환경의 실행 결과 중 일부가 아래와 같습니다:

STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for Solaris: Version 9.2.0.6.0 - Production
Start Date                25-OCT-2005 10:26:47
Uptime                    0 days 13 hr. 8 min. 36 sec
Trace Level               off
Security                  ON

마지막 라인(Security ON)에서 패스워드가 설정되었음을 확인할 수 있습니다.

Oracle Database 10g에서는 설정 방법이 조금 다릅니다. 10g의 경우 오라클 소프트웨어의 소유자만이 패스워드 없이 리스너를 실행할 수 있도록 설정되어 있습니다. 따라서 패스워드가 설정된 경우, 다른 사용자들은 패스워드를 입력한 경우에만 리스너의 실행이 가능합니다. STATUS 설정값의 확인 결과가 아래와 같습니다:

STATUS of the LISTENER
------------------------
Alias                     LISTENER_ODSPDB02
Version                   TNSLSNR for HPUX: Version 10.1.0.4.0 - Production
Start Date                16-OCT-2005 05:58:35
Uptime                    9 days 17 hr. 44 min. 41 sec
Trace Level               off
Security                  ON: Local OS Authentication

마지막 라인(ON: Local OS Authentication)에서 패스워드가 설정되지 않았음을 알 수 있습니다. 패스워드가 설정된 경우에는 아래와 같이 표시됩니다:

Security                  ON: Password or Local OS Authentication

“Password”라는 단어를 통해 패스워드가 설정되어 있음을 알 수 있습니다.

주의 사항
Oracle Database 10g 및 이후 버전에서는 특별히 주의할 사항이 없습니다. 사용자 인증을 위해 OS 인증이 사용되며 리스너의 시작/중단을 위해 패스워드를 입력할 필요가 없습니다. Oracle9i 및 이전 버전에서는 패스워드를 입력해 주어야 하며 따라서 스크립트에도 패스워드를 포함시켜야 합니다.

Action Plan
IF <Oracle Database 10g 및 이후 버전인 경우> THEN
  소유자를 제외한 모든 사용자의 권한을 제거합니다.
ELSE
  소유자를 제외한 모든 사용자의 권한을 제거합니다.
리스너의 패스워드를 설정합니다.
END IF


1.7 리스너의 보호

배경 정보
시스템에 침입하기 위한 방법으로 리스너에 매우 긴 문자열을 전송하여 버퍼 오버플로우를 유도하는 테크닉이 자주 사용됩니다. 또 다른 침입 경로로 lsnrctl 유틸리티의 SET DISPLAYMODE VERBOSE 명령을 사용하여 컴포넌트의 목록을 확인하는 방법이 있습니다. 이 경우 해커는 취약한 머신에서 lsnrctl을 실행하여 설정을 조작한 후, 이를 이용하여 타겟 서버의 리스너에 접근합니다. 그 예가 아래와 같습니다:

LSNRCTL> set trc_level support
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=prolin1)(PORT=1521)))
LISTENER parameter "trc_level" set to support
The command completed successfully
LSNRCTL> set trc_directory /tmp
Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=prolin1)(PORT=1521)))
LISTENER parameter "trc_directory" set to /tmp
The command completed successfully

트레이스 레벨이 SUPPORT로 설정되어 있으므로 리스너는 해커에게 유용한 여러 가지 정보를 출력합니다. 또 트레이스 파일이 /tmp 디렉토리에 저장되므로 조회하기가 쉽습니다. 해커는 이러한 정보를 서버에 직접 로그인하지 않고도 파악할 수 있습니다.

전략
리스너를 보호하기 위한 최상의 대안은 패스워드를 설정하는 것입니다. 또는 lsnrctl 유틸리티를 사용하여 리스너 매개변수의 설정 기능을 제한할 수 있습니다. 이 경우, 매개변수를 변경하기 위해서는 리스너 설정 파일을 변경한 후 다시 로드해야 합니다. 매개변수 설정을 제한 하려면 listener.ora 파일에 아래 라인을 추가하면 됩니다:

ADMIN_RESTRICTIONS_LISTENER = ON

그런 다음 리스너를 재시작합니다. 이제는 lsnrctl 프롬프트에서 SET 커맨드를 사용할 수 없습니다. 실행 예가 아래와 같습니다:

LSNRCTL> set trc_directory /hacker_dir
Connecting to (ADDRESS=(PROTOCOL=IPC)(KEY=PROPRD1))
TNS-12508: TNS:listener could not resolve the COMMAND given

위에서 볼 수 있듯 TNS-12508 에러가 발생하였습니다. 이제부터는 설정 값을 변경하려면 listener.ora 파일을 수정하고 커맨드를 다시 로드해야 합니다.

LSNRCTL> reload

이 방법은 모든 오라클 버전에 동일하게 적용됩니다.

패스워드를 사용하여 리스너를 보호하는 경우에도, 이 테크닉을 추가로 적용하여 해커의 리스너 접근 능력을 제한할 수 있습니다. 특히 오라클 소프트웨어 소유자에게는 리스너 패스워드를 요구하지 않는 Oracle Database 10g 환경에서 이 방법이 유용하게 사용됩니다.

주의 사항

특별히 주의할 사항은 없습니다. 사용자가 온라인 상태에서 매개변수를 편집하는 경우는 거의 없으며, 대부분 listener.ora를 편집하고 리스너를 다시 로드하는 방법을 사용하기 때문입니다.
하지만 다른 서버의 리스너를 관리하기 위해 원격 리스너 컨트롤 기능을 사용할 수 없다는 점을 참고해야 합니다. 그 대신, 원격 서버에 로그온한 후 listner.ora 파일을 수정하고 리스너를 다시 로드하는 방법을 사용해야 합니다 (실제로도 이러한 방법이 권장됩니다).

실행 계획
  1. listener.ora 파일에 ADMIN_RESTRICTIONS_LISTENER = ON 매개변수를 설정합니다.
  2. lsnrctl reload 명령을 실행하여 리스너를 다시 로드합니다.


1.8 과도한 권한의 제한

배경 정보

일반적으로 사용자들은 자신이 주로 수행하는 작업에 관련된 권한만을 필요로 합니다. 각 사용자에게 꼭 필요한 권한만을 부여하는 방법이 현실적인 대안이 되기는 어렵겠지만, 차선책으로 사용자들에게 부여된 강력한 권한 중 일부를 제거하는 작업이 필요할 수 있습니다.

사용자들에게 불필요한 강력한 권한의 예로 “CREATE ANY TABLE”이 있습니다. 사용자는 이 명령을 사용하여 자신의 스키마 뿐 아니라 다른 어떤 스키마에서도 테이블을 생성할 수 있습니다. 사용자들이 이 권한을 실제로 사용하는 경우는 매우 드물기 때문에, 권한을 취소하더라도 문제는 없습니다. 반면, 사용자 세션에서 함수 기반 인덱스 또는 MV(materialized view)를 이용한 쿼리 재작성을 허용하는 “QUERY REWRITE”와 같은 권한은 상대적으로 무해하다고 볼 수 있습니다.

전략
먼저, 위험하지 않다고 판단되는 권한의 목록을 작성합니다 (CREATE TYPE, CREATE SESSION 등). 필자는 이 목록에 “UNLIMITED TABLESPACE”를 포함시켰습니다만 동의하지 않는 분도 계실 것입니다.

set pages 50000
break on privilege skip 1

select privilege, grantee, admin_option
from dba_sys_privs
where privilege not in
(
   /* list any other privilege here you don't find
       "sweeping"
   */
   'ALTER SESSION',
   'QUERY REWRITE',
   'CREATE DIMENSION',
   'CREATE INDEXTYPE',
   'CREATE LIBRARY',
   'CREATE OPERATOR',
   'CREATE PROCEDURE',
   'CREATE SEQUENCE',
   'CREATE SESSION',
   'CREATE SNAPSHOT',
   'CREATE SYNONYM',
   'CREATE TABLE',
   'CREATE TRIGGER',
   'CREATE TYPE',
   'CREATE USER',
   'CREATE VIEW',
   'UNLIMITED TABLESPACE'
)
and grantee not in
 ('SYS','SYSTEM','WKSYS','XDB',
  'MDSYS','ORDPLUGINS','ODM','DBA')
    /* Place all the user names you want to exclude */
order by privilege, grantee
/

쿼리 실행 결과의 일부가 아래와 같습니다:

PRIVILEGE                   GRANTEE                        ADM
--------------------------- ------------------------------ ---
ADMINISTER DATABASE TRIGGER EXFSYS                         NO
                            IMP_FULL_DATABASE              NO

ADMINISTER RESOURCE MANAGER EXP_FULL_DATABASE              NO
                            IMP_FULL_DATABASE              NO

ALTER ANY MATERIALIZED VIEW DWETL                          NO
                            REPORTMAN                      NO

ALTER ANY OUTLINE           REPORTMAN                      NO

ALTER ANY PROCEDURE         IMP_FULL_DATABASE              NO
                            QCO                            NO

ALTER ANY RULE              CDC_PUB                        YES

ALTER ANY RULE SET          CDC_PUB                        YES

ALTER ANY TABLE             IMP_FULL_DATABASE              NO
                            CNSMP                          NO
                            QCO                            NO

ALTER ANY TRIGGER           IMP_FULL_DATABASE              NO
                            QCO                            NO
                            VCHANG                         NO

ALTER ANY TYPE              IMP_FULL_DATABASE              NO

ALTER SYSTEM                ORADBA                         NO
                            QCO                            NO

ALTER TABLESPACE            QCO                            NO

ALTER USER                  QCO                            NO
                            SYSMAN                         NO

ANALYZE ANY                 AFFMAN                         NO
                            ARAO                           NO
                            CONCASTER                      NO
CREATE ANY SYNONYM          ATHOTANG                       YES
                            ARUP                           YES
                            IMP_FULL_DATABASE              NO
                            DB_MONITOR                     YES
                            QCO                            YES
                            RCHUNG                         YES
                            SPOT                           YES

CREATE ANY TABLE            IMP_FULL_DATABASE              NO
                            CNSMP                          NO
                            QCO                            NO
                            SYSMAN                         NO

DROP ANY TABLE              ATHOTANG                       YES
                            IMP_FULL_DATABASE              NO
                            CNSMP                          NO
                            QCO                            YES
_ 후략 _

위 실행 결과에서 DROP ANY TABLE과 같은 권한은 일반 사용자들에게 전혀 필요하지 않은 것으로 볼 수 있습니다.
이제, 어떤 사용자들이 이러한 권한을 부여 받고 있는지 확인해 봅시다.

  • MP_FULL_DATABASE는 전체 데이터베이스의 임포트를 위한 역할로, 일반적으로 임포트 작업을 수행해야 하는 DBA 또는 다른 사용자에게 할당됩니다.
    이 권한은 상황에 따라 필요할 가능성이 있습니다.
  • QCO는 데이터베이스 관리를 위해 사용되는 툴인 Quest Central for Oracle의 약자입니다. 이 권한도 필요할 가능성이 있습니다.
  • NSMP, ATHOTANG 사용자는 DBA가 아닌 이상 과도한 권한을 부여해 줄 필요가 없습니다. 따라서 권한을 제거해 줄 필요가 있어 보입니다.
Implications
이 작업은 결코 한 번에 실행해서는 안됩니다. 사용자에게서 권한을 제거하기 전에 그 영향을 주의 깊게 분석해야 합니다.

의심스러운 부분이 있다면, 해당 ID를 사용하는 실제 사용자와 인터뷰를 하는 것이 가장 좋은 방법입니다. 예를 들어, ATHOTANG 사용자는 실제로 테이블의 DROP 작업을 수행할 필요가 없음에도 자신이 그러한 권한을 필요로 한다고 생각하고 있을 수도 있습니다. (결코 놀랄 일이 아닙니다. 이런 경우는 매우 흔합니다.)

실행 계획
이 작업을 수행하기 위해서는 어느 정도 사전 계획이 필요합니다. 따라서 실제 조치는 다음 단계로 연기하고 여기에서는 필요한 정보만 수집해 두도록 합니다.


1.9 DBSNMP 패스워드의 변경

배경 정보

여러분들도 잘 알고 있다시피, Oracle Intelligent Agent는 Oracle Enterprise Manager와의 커뮤니케이션을 통해 데이터베이스, 리스너, 서버 등의 컴포넌트에 대한 정보를 전달합니다. 데이터베이스에 대한 정보를 얻기 위해 에이전트는 특정 사용자 아이디를 사용하여 데이터베이스에 접근해야 합니다. 디폴트 설정에서 사용되는 사용자 아이디는 DBSNMP입니다.

데이터베이스가 생성되는 과정에서 dbsnmp의 패스워드는 “dbsnmp”로 설정됩니다. 이 사용자는 UNLIMITED TABLESPACE, SELECT ANY DICTIONARY(다이내믹 성능 뷰와 데이터 딕셔너리 뷰에 대한 쿼리), ANALYZE ANY DICTIONARY(시스템 오브젝트의 분석)와 같은 일부 강력한 권한을 소유하고 있습니다. 침입자들은 이 아이디와 패스워드를 이용하여 데이터베이스의 백도어 진입을 시도하기도 합니다. 따라서 매우 중요한 보안 취약점으로 고려되어야 합니다.

전략
DBSNMP의 패스워드를 “dbsnmp”가 아닌 다른 것으로 바꾸어 주어야 합니다. 하지만 패스워드가 에이전트 설정 파일에도 저장되어 있기 때문에, 단순히 데이터베이스 레벨에서 패스워드를 변경해 주는 것만으로는 충분치 않습니다. 설정 파일에서도 새로운 패스워드를 사용하도록 업데이트해 주어야 합니다. Oracle Database 10g 환경에서의 패스워드 변경 방법이 아래와 같습니다.
  1. 1. 먼저 DBSNMP 사용자의 패스워드를 다른 것(예: TopSecret:)으로 바꾸어 줍니다:
    SQL> alter user dbsnmp identified by topsecret;
  2. Oracle Agent Home이 설치된 디렉토리로 이동합니다 (예: /u01/app/oracle/10.1/gridc).
  3. <hostname>/sysman/emd 디렉토리(<hostname>은 호스트 또는 서버 이름)로 이동합니다. (예를 들어 서버 이름이 prolin1이라면, 디렉토리는 prolin1/sysman/emd입니다.)
  4. 디렉토리에서 targets.xml 이라는 이름의 파일을 찾아 다른 이름으로 복사합니다 (예: targets.xml.old).
  5. 5. targets.xml 파일을 열고 "dbsnmp" 단어를 검색합니다. 아래와 같은 내용을 확인할 수 있을 것입니다:
    <Target TYPE="oracle_database" NAME="PROPRD1_prolin1">
    <Property NAME="MachineName" VALUE="192.168.101"/>
    <Property NAME="OracleHome" VALUE="/u01/app/oracle/10.1/db1"/>
    <Property NAME="Port" VALUE="1521"/>
    <Property NAME="Role" VALUE="NORMAL"/>
    <Property NAME="SID" VALUE="PROPRD1"/>
    <Property NAME="ServiceName" VALUE="PROPRD"/>
    <Property NAME="UserName" VALUE="dbsnmp"/>
    <Property NAME="password" VALUE="3797cf30e7c4a9c6" ENCRYPTED="TRUE"/>
    <CompositeMembership>
    <MemberOf TYPE="rac_database" NAME="PROPRD" ASSOCIATION="cluster_member"/>
    </CompositeMembership>
    </Target>
    
  6. 아래 라인을 주목하십시오:
    <Property NAME="password" VALUE="3797cf30e7c4a9c6" ENCRYPTED="TRUE"/>

    이 곳에서 패스워드를 설정해 주어야 합니다. 위 라인을 아래 라인으로 대체합니다:

    <Property NAME="password" VALUE="topsecret" ENCRYPTED="FALSE"/>

    여기서 ENCRYPTED를 FALSE로 설정하였음에 주의합니다.

  7. RAC 데이터베이스의 경우, 파일에 위 라인이 두 번 나오므로, 두 개의 항목을 모두 변경해 주어야 합니다. "password" 단어를 기준으로 검색하면 두 항목을 쉽게 찾을 수 있습니다.

  8. 이제 아래 명령을 실행하여 에이전트를 중단시킵니다:
    /u01/app/oracle/10.1/gridc/bin/emctl stop agent
  9. 에이전트를 재시작합니다:
    /u01/app/oracle/10.1/gridc/bin/emctl stop agent
  10. 10. 에이전트를 재시작할 때, 설정 파일에 저장된 패스워드가 자동으로 암호화됩니다. targets.xml 파일을 다시 열어 보면 위 라인이 아래와 같이 수정되어 있음을 확인할 수 있습니다:
    <Property NAME="password" VALUE="3797cf30e7c4a9c6" ENCRYPTED="TRUE"/>

    일반 텍스트로 작성된 값이 암호화된 값으로 변경되어 있습니다.

  11. 이제 에이전트에 새로운 패스워드를 설정하는 작업을 완료하였습니다.

  12. Grid Control 대신 스탠드얼론 Database Console을 사용하는 경우에도 유사한 방법을 사용합니다. 단 2 단계에서 Agent Home이 아닌 ORACLE_HOME으로 이동해야 한다는 것이 다른 점입니다.

주의 사항
특별히 주의할 사항은 없습니다.

Action Plan

  • DBSNMP 사용자의 패스워드를 변경합니다.
  • 에이전트 파일의 패스워드를 업데이트합니다.

본 문서에서 제공되는 정보는 참고 용도로 작성되었으며 오라클에 의해 검증되지 않았습니다. 따라서 실제 적용 작업에 대한 책임은 전적으로 사용자에게 있습니다! 어떠한 경우에도 본 문서의 내용이 컨설팅 또는 서비스의 일부로 해석되어서는 안됩니다.

2 단계로 가기