메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

자바 애플리케이션 보안 요구사항의 발견 - 1

한빛미디어

|

2007-02-22

|

by HANBIT

13,044

제공 : 한빛 네트워크
저자 : Mark Petrovic
역자 : 김형욱
원문 : http://www.onjava.com/pub/a/onjava/2007/01/03/discovering-java-security-requirements.html

애플리케이션의 보안이 쉽지 않다는 것은 잘 알려진 사실이지만 보안 장애의 위험을 줄일 수 있는 방안들이 있다는 것 역시 주지의 사실이다. 만약 네트워크 엔지니어라면 네트워크 분할(partitioning)과 패킷 필터(packet filter)쪽을 집중적으로 파면 될 것이고 C 프로그래머라면 buffer overflow가 발생하지 않도록 해야 할 것이다. 또 자바 프로그래머라면 애플리케이션이 Security Manager(보안 관리자)의 보호 하에서 돌아가게 하는 것을 생각해 볼 수 있다. 결국 어떤 경우에서건 예기치 못한 시스템 장애에 대한 효율적인 대응으로 모범 사례(best practices)를 활용하게 된다고 할 것이다.

자바 애플리케이션의 보안에 대해 제공되는 사항은 문서화가 잘 되어 있으며 한편 이는 본 문서에서 논의되는 사항보다 상위 개념이라고 할 수 있다. 본 문서에서는 자바 보안 아키텍처(Java Security Architecture)의 일부분이라고 할 수 있는 자바 Security Manager에 초점을 두고 논의를 진행하도록 하겠다.

Security manager는 java.lang.SecurityManager 클래스(혹은 이를 확장한 클래스)로서 특정 애플리케이션에서 행하는 작업들이 런타임시 허용되는지 여부를 검사하는 역할을 한다. 애플리케이션이 일단 Security Manager의 제어 하에서 실행되면 관련 보안 정책(Security Policy)에 의해 허용된 작업들만 수행될 수 있으며 이 때 정책(policy)은 기본적으로 일반 텍스트 문서로 된 정책파일(policy file)로 기술된다. 허용여부가 문제되는 해당 작업으로 일부만 언급해보면 특정 디렉토리에 파일을 쓰거나 시스템 속성값을 쓰는 일, 특정 호스트에 네트워크 연결하는 일 등이다. 자바 애플리케이션이 Security Manager 하에서 돌아가게 하는 건 그저 간단한 JVM command line option만으로도 가능하며 정책 파일 역시 어느 텍스트 에디터로도 쉽게 만들 수 있다.

보안 정책 파일을 편집하고 다양한 관련 규칙들을 추가하는 것은 그다지 어렵지 않은 반면에 policy right(정책 권한)을 얻는 것은 다소 많은 수고를 요할 수 있다. 또 아무도 우리에게 policy right를 얻어다 주지도 않는다. 그럼에도 불구하고 우리는 이 정책(policy)이 어떤 식으로 되어야 하는지에 대한 이해를 돕기 위해 툴을 사용할 수 있으며 그러한 툴을 개발하고 사용하는 것이 바로 우리가 이 문서에서 다룰 부분이다. 일단 그 툴을 통해 광범위하면서도 정제된 정책을 얻게 되면 우리는 그 정책을 제품의 runtime policy(런타임 정책)을 개발하는데 있어 출발점으로 삼거나 또는 애플리케이션의 보안 요구 사항을 보다 잘 이해하기 위한 공부의 목적으로 활용할 수도 있을 것이다.

본 문서의 핵심 코드라 할 수 있는 security manager는 Sun의 JSE 5 JVM이 있어야 사용이 가능한데 이는 security manager가 특정 Java 시스템 클래스로부터 private member data를 얻기 위해서는 Java Reflection API에 의존해야 하기 때문이다. Security manager가 작동하기 위해 필요로 하는 특정 private member data에 대한 접근 권한을 가지고 있지 않아 Reflection API를 사용해야 하며 그 결과 security manager는 그것을 구동시키는 JVM의 내부에 강하게 구속된다. 하지만 이것은 컴포넌트 제품이 아닌 개발툴 이므로 심각한 문제는 아니라고 할 것이며 일단 security manager가 어떤 정책을 출발점으로 제안하면 우리는 그 정책을 가지고 어떠한 JVM에서건 애플리케이션을 돌릴 수 있다.

기본(Default) Java Security Manager

우리가 요즘 책이나 문서 등에서 보고, 토론하며 작성해보는 대부분의 자바 코드는 security manager와 상관없이 돌아가는 애플리케이션이다. 이러한 애플리케이션은 디스크, 네트워크, 애플리케이션의 종료 등을 포함한 모든 자원에 대해 모든 접근 권한을 가지게 된다. 하지만 이 접근권한은 손쉽게 제한될 수 있다. JVM command line에 그저 -Djava.security.manager 옵션을 넣는 것만으로 애플리케이션은 기본 Java Security manager 아래서 돌아가게 된다.

사용자의 home directory를 읽고 출력하도록 설계된 아래의 간단한 애플리케이션을 보자.
public class PrintHome {
   public static void main(String[] argv) {
      System.out.println(System.getProperty("user.home"));
   }
}
이제 코드를 컴파일하고 이를 기본 Security manager를 써서 실행시켜보자.
$ cd $HOME/Projects/CustomSecurityManager

$ javac PrintHome.java

$ java -Djava.security.manager PrintHome
Exception in thread "main" java.security.AccessControlException: 
access denied (java.util.PropertyPermission user.home read)
...
at PrintHome.main(PrintHome.java:5)
그 결과 애플리케이션은 user.home 속성값을 읽고 출력하는 데 실패한 걸 볼 수 있다. (읽기 편하게 하기 위해 대부분의 stacktrace는 생략했다.) 기본 security policy로 돌아가는 기본 security manager가 user.home 속성값에 대한 접근을 금지하여 애플리케이션이 제대로 실행될 수 없었던 것이다. 이러한 권한은 런타임 정책 파일(runtime policy file)에 명시적으로 수여되어야 한다.

아래와 같이 하나의 규칙을 가진 policy.txt라는 정책 파일(policy file)을 만들자.
grant codeBase "file:/home/cid/Projects/CustomSecurityManager/"{
   permission java.util.PropertyPermission "user.home", "read";
};
그리고 이 정책 파일을 참조하여 애플리케이션을 다시 실행시키면 user.home에 대한 접근 권한의 문제가 해결된다.
$ java -Djava.security.policy=policy.txt -Djava.security.manager PrintHome
 /home/cid
java.security.policy=policy.txt라는 옵션을 설정함으로써 JVM에게 정책파일에 대해 언급했다는 걸 알아두자. 또한 PrintHome 클래스는 /home/cid/Projects/CustomSecurityManager 디렉토리 안에 있는 것으로 가정했다. 앞에서 만든 policy.txt 파일에 있는 규칙은 위 디렉토리에 속해있는 파일 중의 어느 코드라도 user.home의 속성값을 읽을 수 있도록 되어있으며 그 결과 PrintHome은 우리가 의도한 대로 실행될 수 있는 것이다. 여기서 코드가 포함된 파일을 codebase라 부르는데 결국 class 또는 jar 파일의 형태를 띄게 된다. (역자 주 – 여기서는 PrintHome 클래스)

보안 정책 프로파일러 : ProfilingSecurityManager

policytool과 같이 보안 정책 생성에 유용한 유틸리티가 있음에도 불구하고, 이미 언급한 바와 같이 정책 파일을 수동으로 만드는 것이 어려운 일은 아니다. 그리고 정책 파일에서 허용되는, 굉장히 강력할 뿐만 아니라 명확하고 효율적인 규칙을 만들 수 있게 해주는 문법에 대한 지름길(Syntax shortcuts)도 있다. 이러한 진보된 규칙 표기법(notation)을 사용하면 일례로 codebase URL이 전체 디렉토리 트리를 순환적으로 가리키도록 기술할 수도 있다. 하지만 이 경우 그 유용성과 편리함에도 불구하고 그 정책을 보는 사람에게는 애플리케이션의 자원 요구에 대한 진정하고 정제된 심각성을 깨닫지 못하게 하는 문제를 낳기도 한다.

따라서 우리의 목표를 2가지로 잡았는데 하나는 애플리케이션이 보안 정책에 따라 돌아가게 하거나 적어도 우리가 애플리케이션의 보안 요구를 결정하는 것이다. 그리고 다른 하나는 그러한 요구를 결정하는 데 있어 프로그램적인 방법을 사용하는 것이다.

이러한 목표를 염두에 두고 자체 제작한 security manager인 secmgr.ProfilingSecurityManager를 소개한다. 이 클래스는 java.lang.SecurityManager를 확장한 클래스이지만 이제까지 논의된 방식으로 보안 정책을 강제하지는 않는다. 대신에 런타임시 애플리케이션의 모든 요청에 대해 접근이 허용되었을 경우 보안 정책이 어떻게 될지를 알려준다. 그러면 우리는 그 내용을 런타임 보안 정책의 출발점으로 삼을 수 있으며 결국 우리의 2가지 목표는 모두 달성된다.

ProfilingSecurityManager를 사용하기 위해 먼저 소스 코드(문서 말미의 참고 자료 참조)를 컴파일하고 해당 클래스 파일만으로 구성된 jar 파일을 만들자. 이렇게 ProfilingSecurityManager 만 따로 jar 파일로 만들면 이 jar 파일과 관련된 작업들로 인해 만들어진 규칙들을 걸러내고 감출 수 있는 이점이 있다. ProfilingSecurityManager는 아래 코드를 통해 자신의 유일무이한 codebase를 알 수 있다.
if( url.toString().equals(thisCodeSourceURLString) ) {
   return null;
}
그리고 이로 인해 자기 자신에 대한 리포팅을 방지할 수 있다. 소스를 컴파일하고 jar를 만들어보자.
$ mkdir -p classes lib

$ rm -rf classes/* lib/*

$ javac -d classes ProfilingSecurityManager.java

$ jar cf lib/psm.jar -C classes secmgr/manager

$ rm -rf classes/secmgr/manager
진도를 더 나가기 전에 ProfilingSecurityManager를 애플리케이션의 security manager로 활성화시키는 방법에 대해 얘기를 해야 할 것 같다. 앞으로 되돌아가 보면 우리는 시스템 속성을 -Djava.security.manager로 하고 특정한 속성값을 주지 않음으로써 애플리케이션이 기본 자바 security manager에 의해 돌아가도록 했었다. 이제 한 발짝 더 나아가 Djava.security.manager=secmgr.ProfingSecurityManager 라고 시스템 속성에 값을 할당함으로써 ProfingSecurityManager를 security manager로 설정해보자. 이렇게 활성화된 ProfilingSecurityManager는 System.out에 정책 파일에서 필요로 하는 규칙 즉, 애플리케이션이 보안 위반 예외(security violation exception) 없이 실행되도록 하는데 필요한 규칙을 기록 할 것이다. 하지만 이러한 규칙들은 애플리케이션이 ProfilingSecurityManager 아래서 그 실행을 마치기 전까지는 최종적이고 사용 가능한 형태로 처리될 수 는 없다. 왜냐고? 그 때서야 비로소 애플리케이션이 검사된 자원에 대한 접근 요청을 끝마쳤다는 것을 알 수 있기 때문이다. 따라서 애플리케이션이 ProfilingSecurityManager 아래서 실행되는 것을 마쳤을 때 규칙을 만들고 그것을 정돈하기 위해 parsecodebase.pl 이라는 간단한 Perl Script를 제공한다. 이를 통해 규칙들을 모으고, 그 포맷을 정할 뿐만 아니라 규칙들을 codebase에 따라 정렬하고 그룹화하며 읽기 쉬운 형태로 출력할 수 있다.

ProfilingSecurityManager를 security manager로 지정하고 parsecodebase.pl에 의해 만들어진 규칙들을 가지고 PrintHome 애플리케이션을 실행시키면 그 결과는 아래과 같다.
$ java -cp .:lib/psm.jar -Djava.security.manager=secmgr.ProfilingSecurityManager PrintHome
  > raw.out

$ parsecodebase.pl < raw.out > policy.txt

$ cat policy.txt
grant codeBase "file:/home/cid/Projects/CustomSecurityManager/" {
   permission java.util.PropertyPermission "user.home", "read";
};

$ java -cp . -Djava.security.manager -Djava.security.policy=policy.txt PrintHome 
/home/cid
ProfilingSecurityManager가 앞에서 언급한 우리의 두 가지 설계 목표를 모두 충족시켰음을 다시 확인할 수 있다.
  • 애플리케이션이 잘 정의되고 애플리케이션에 적합한 정책에 따라 security manager 아래서 실행된다.
  • 해당 정책은 프로그램적으로 결정한다.
ProfilingSecurityManager는 어떻게 작동될까? ProfilingSecurityManager는 java.lang.SecurityManager의 두 가지 형태의 checkPermission 메서드를 오버라이드(override)한다. 이 메서드들은 애플리케이션이 접근을 요청한 자원이나 작업을 검사하기 위한 주요 관문(chokepoint)이다. 이렇게 오버라이드 된 checkPermission 메서드는 필요한 규칙을 만들고 그것을 출력하며 항상 exception을 발생시키지 않고 (본질적으로 접근이 허용되었다는 것을 의미) return 된다.
TAG :
댓글 입력
자료실

최근 본 책0