소프트웨어는 보통 각각 전문화(specialized)된 서비스를 제공하는 여러 서브젝트(혹은 컴포넌트, 프로시저, 함수, 서브루틴, 오브젝트)로 구성된다. 안전한 시스템의 경우 이런 서브젝트가 일을 수행하기 위해서는 이를 사용하는 클라이언트가 적절한 권한을 가지고 있어야 한다. 이 경우 서비스 서브젝트는 대리인이라고 불리는데, 클라이언트에게 위임 받은 권한으로 필요한 일을 해주기 때문이다.

혼동된 대리인(confused deputy)는 서브젝트가 클라이언트의 권한을 사용해야 하는 경우에 실수로 자기 자신의 권한으로 일을 수행했을 경우를 일컫는다. 클라이언트는 권한이 없지만 대리인은 권한이 있는 일을 부탁했을 때 대리인이 혼동하여 이 일을 수행해 준다면 보안 문제가 발생하게 되는 것이다. [위키피디아]

혼동된 대리인 문제는 소프트웨어와 시스템의 권한 분리에서 기인한다. 유닉스와 윈도를 비롯한 대부분의 운영체제는 접근 제어 리스트(access control list, ACL)를이용해 어떤 서브젝트가 어떤 권한을 가지고 있는지 검사하는데, 소프트웨어는 별다른 권한 검사를 할 필요가 없다는 면에서 관심의 분리(separation of concerns)는 확실히 보안을 강화시키는 긍정적인 효과가 있다. 하지만 이 때문에 대리인은 스스로 필요한 일을 하는 경우와 클라이언트의 일을 대신해 주는경우를 구분하기 힘들게 되었다.

이런 서브젝트의 구체적인 예로 유닉스(UNIX) 시스템의 passwd 프로그램이 있다. passwd는 프로그램을 이용해 유닉스 시스템의 사용자는 자기 자신의 암호 파일을 고치게 되는데, 이 경우 클라이언트는 /etc/passwd 파일을 고칠 권한이 없다. 대리인인 passwd 프로그램은 setuid root인 프로그램이므로 루트 권한을 가지고 있다. 여기서 의도한 바는 각각의 유저는 자기 자신의 암호만 고칠 수 있도록 하는 것이지만, 이 방식은 전적으로 passwd 프로그램의 입력 값 검사(암호 검사)에 의존하게 된다. 버퍼오버플로우(buffer overflow)를 일으켜서 암호 검사만 우회할 수 있다면(대리인을 속일 수만 있다면) passwd 프로그램(대리인)의 모든 권한을 손에 넣을 수 있게 된다.

또 다른 예로는 서버 프로그램이 있다. 서버 프로그램은 네트워크로 들어오는 요청을 받아서 처리해준다는 면에서 대표적인 대리인에 해당한다. 서버 프로그램도 UID를 가지고 있으면 해당 UID의 권한으로 프로세스가 수행된다. 만약 루트 권한으로 서버가 돌고 있다면 이 대리인의 권한은 루트인 셈이다. 문제는 서버에 서비스를 요청해 온 클라이언트는 이러한 서버 프로그램을 혼동시켜 실제로는 권한이 없는 일을 수행하도록 부탁할 수 있다는 점이다.

이런 문제를 해결하기 위해서는 케이퍼빌러티(capability)라는 보안 메커니즘이 필요하다.


char []와 char *

Posted 2006. 9. 9. 21:27
어제 후배 녀석과 이야기하다가 char[]와 char *의 차이점에 대한 이야기를 하게 되었다. C 언어를 처음 가르칠 때 편의상 char[]와 char *를 혼용해서 쓰도록 가르친 탓인지 의외로 컴공과 3-4학년 이상되는 학생들조차 그 차이점을 제대로 모르고 있는듯 싶다.


char str[] = "abc";


char *str = "abc";

printf("%s", str) 하면 똑같이 abc가 찍혀나오니깐 별 차이가 없다고 생각할 수도 있겠지만 이 두 선언은 엄연히 다르다. 위 코드를 gcc -S 옵션으로 어셈블리로 생성해보면 두 코드 모두 "abc"를 .rodata 섹션에 할당한다,

gcc2_compiled.:
.section .rodata
.LC0:
.byte 0x61,0x62,0x63,0x0


여기서 .rodata는 읽기 전용(read only) 데이터를 말한다. 그렇다면 다음과 같은 코드를 실행하면 어떻게 될까?

int main()
{
    char *str = "abc";
    str[0] = 'b';
}


str은 .rodata 영역에 있는 "abc"에 대한 포인터이므로 이 값을 바꾸려고 하면 다음과 같이 오류가 발생한다.

./a.out
Bus error (core dumped)



str[]의 경우는 어떨까?

int main()
{
    char str[] = "abc";

    str[0] = 'b';
}


이 프로그램은 문제없이 수행되며 str을 출력해보면 "bbc"가 나온다. 이런 차이가 나는 이유는 str[]의 경우 함수 호출 시에 .rodata의 "abc" 문자열을 함수의 스택으로 복사해오기 때문이다.

또 하나의 차이점이 있는데, char *의 경우 포인터 변수를 할당하여 주소 값을 가지고 있는데 비해 char[]의 경우 str 그 자체가 주소값이 된다.

C++ try/catch

Posted 2006. 9. 9. 10:22
보통 C++ 코딩을 잘 안하는데, 이번에 M 회사 프로젝트하면서 리눅스에서 C++로 어플리케이션을 개발하게 되었습니다. 그래도 C와는 다르게 try/catch를 통한 예외 처리를 할 수 있어서 좋았습니다. 그런데 오랫동안 자바 코딩을 해와서 그런지 다음과 같은 실수를 저질렀습니다.

<예제>
int foo()
{
    throw new Exception();
}

...
try {
    foo();
} catch (Exception& e) {
}


이상하게 catch가 안 되길래 봤더니 C++의 경우 throw Exception()을 써야했던 것이더군요.C++은 throw로 던질 수 있는 타입에 제한이 없다는 점이 문제였습니다. 자바를 비롯한 여러 언어는 최상위 예외클래스(자바의 경우 java.lang.Throwable)이 존재하는데 비해서, C++은 throw 1 같이 primitive타입까지 던질 수 있는 것이죠.

<수정>
int foo()
{
    throw Exception();
}

...
try {
    foo();
} catch (Exception& e) {
}
« PREV : 1 : ··· : 75 : 76 : 77 : 78 : 79 : 80 : 81 : 82 : NEXT »