Skip to content

무었을 지키기 위한 보안을 하고 있을까?

올해의 첫글은 보안에 대하 제 생각을 정리해 보고자 합니다.

여러 회사를 거치면서 전산팀을 여러번 관리하면서 매번 느끼는 점인 보안의 중요성 및 그 실효성에 대한 제 의문점이 있습니다.

많은 시스템 관리자들은 회사의 보안을 위해서 백신 및 파이어월 등을 매우 중요하게 생각하는것을 많이 보았다.

나는 그들에게 한가지 질문을 던진적이 있었다.
도데체 무얼 지키고 있냐고 물었다. 관리자들은 대부분 회사의 정보 및 개인의 보안을 위한다고 대답을 했으면 그 정보 및 개인의 보안이 얼마의 가치가 있는지에 대해선 정확한 대답을 못했다.

물론 회사의 정보 및 여러 시스템의 침입을 막기위한 방법으로 보안 및 백업은 100번을 강조해도 지나치 않는다. 하지만 무었을 지키고 있는 정도는 다시한번 생각해야 한다는게 내 생각이다.

언젠가 보안관련 세미나에서 들었던 내용인걸로 기억하는데
해커 정확히 말하면 크래커 의 시스템 침입의 95% 는 내부 관리소홀에 의한 내부 소행이면 4% 정도는 지나가다 들린 막연힌 네트웍 침입 프로그램이며 실제 해킹의 의도를 가진 해커는 1% 내외라는 것이다.

요즘들어 특히 바이러스나 파이어월에 대한 관심도 높아지고 XP의 서비스팩2를 발표하는 MS의 정책에서도 나오듯이 사용자의 편의와 보안중 하나를 선택하라면 보안을 선택하겠다는 MS관련자의 말은 약간에 오해를 불러일으키기 충분하다고 생각한다.

우리가 컴퓨터를 쓰는것은 마치 자동차를 쓰는것 만큼이나 쉽고 흔한일이 되었다. 하지만 자신의 자동차가 엔진오일이나 미션오일 브레이크패드 등도 안갈아주고 있다고 그냥 아무생각없이 자동차를 길에 세우고 있는 사람들을 볼때 얼마나 한심한 눈초리로 보고 있는가 생각해 보자. 또한 자동차 키를 그 안에 꽂아두고서 시동까지 걸어놓고 길에 차를 나두고 몇시간이 업무를 보다가 차량을 잃어버렸다고 치자 이것 정상적인 운전자로 생각하는 사람은 하나도 없다는 점이다.

컴퓨터는 이제 특정한 사람들의 소유물도 특수한 교육을 받은 전문가의 전유물도 아니다. 모든사람의 생활에 없어서는 안될만큼 중요한 자리를 지키고 있는 만큼 그 관리또한 최소한의 책임은 개인들에게 있는것이다.

다시 보안문제로 돌아와서리..
회사에서 보안을 하는 대표적인 이유는 이러한 개인의 문제를 일괄적으로 아주 쉽게 그냥 벽을 막는 절차로 해결할려는 점이다.
실제로 많은 시스템 관리를 위한 파이어월을 지나지 않고도 회사내 접속을 위한 포트 및 아이피는 대부분 열려있다는 점이다.
대문은 절처히 지키지만 옆문은 아주 허술하기 짝이 없는 것이다.

몇가지 개인들이나 네트웍 관리자들이 보안에 노출되는 경우는

1. OS를 최신버젼으로 업그레이드
2. 백신 프로그램을 최신버젼으로 업그레이드
3. Windows 개열의 프로그램에서 폴더를 공유해서 사용
4. 아웃룩이나 아웃룩익스프레스에서 기본 설정으로 메일사용하기
5. 인터넷 사이트 검색중 Activex 설치창을 보면 항상 허용하기
6. 알지못하는 메일에 포함된 프로그램 실행하기
7. 시스템 부팅 및 화면보호기에 암호안걸기
8. 사용하지 않는 포트 열어놓기

나열하자면 더 많겠지만 상기 항목들이 아주 흔히 볼수있는 내용이다.

1, 2번의 경우는 자동 업그레이드 기능으로 귀찮음을 대신할 수 있고 항상 트레이 아이콘상에 지구본이나 깜박이는 백신 프로그램을 좀더 세심히 관찰한다면 실수하지 않을것이다.
3번은 업무 효율성을 위해서 많이 사용하지만..절대 금물인 항목이다 윈도우 네트웍 설계상 폴더 공유는 인터넷에 전체 공유가 되고 있다는 점을 명심해야 한다.
4번은 가능하다면 웹메일을 추천하며 굳이 아웃룩 계열을 쓴다면 가급적 메일보기 옵션에서 선택시 내용 자동보여주기 옵션을 끄고 리스트에서 더블클릭으로 메일 내용이 보이도록 설정하고 쓴느것이 안젆다. 많은 스팸 메일들은 이제 그 메일내용을 보기만 해도 바이러스가 걸리는 경우가 많다.
5, 6번은 절대 필요에 의한 설치 프로그램이 아니라면 습관적으로 일단 No를 하고나서 다시 필요한 경우만 사이트를 Refresh(F5)로 얼마든지 다시 설치가 가능하다.
윈도우를 사용한다면 자리비움시 화면암호를 걸고 이동하는 습관이 중요하다. 화면보호기에서 빠질때도 암호를 입력하는 옵션도 활용하는것이 개인의 자료및 내부 보안에 기본항목이다.
8번은 네트웍을 좀더 잘 아는 경우에 해당하기는 하지만 Linux를 사용하는 관리자들은 사용하지 않는 데몬 및 포트는 기본적으로 막아놓고 상요하는 습관이 중요하다.
특히 telnet보다는 ssh를 이용하며 ssh도 그냥사용하면 telnet과 다름이 없게 되므로 반드시 public key 방식의 접근을 허용하도록 셋팅하는것이 시스템 보안에 문제가 없다.

참고로 나는 상기 항목을 준수한 이후로 한번도 시스템 해킹 및 바이러스는 걸린적이 없었다. 물로 위 항목만 준수하면 아마도 보안에 크게 신경쓰지 않아도 될것이다.

네트웍 관리자들은 무엇을 지킬지도 모르고 그냥 벽만 쌓는것 보다는 상기 항목들을 일반 사용자들에게 교육시키는게 더 안전한 시스템 관리가 될것이다. 정문은 관리자가 지키지만 옆문은 각자가 지켜야 되기 때문이다.

마지막으로 아무리 보안 및 시스템 관리에 철저하다 해도 백업의 중요성은 잊어서는 안됄것이다.

시스템과 데이타는 언제든지 지워도 무방하다는 생각으로 백업을 생활하 하는 것이 해커를 무용지물로 만드는 지름길이다.

C++ 클래스 설계

C++을 사용하면서 자신만의 클래스를 설계하는 경우가 요즘은 참 드문거 같다. 특히 국내 개발사이트들을 찾아다니다 보면…

C++의 핵심이 클래스인데 왜 다들 MFC클래스에만 의존하는걸까?
물론 MFC클래스가 구조적을 상당히 안정되고 그 기능이 매우 잘 정리된 라이브러리임은 부정하지 않는다.

초기 MFC보다 볼란드에선 OWL 이라는 클래스 라이브러리가 MFC와 더불어 윈도우 클래스의 양대산맥이였다. 많은 개발자들은 두 라이브러리중에서 어떤걸 사용해야 할까라는 질문을 많이 했었다. 실제 사용상이나 편의성으로 볼때 OWL이 훨씬 더 좋은 라이브러리지만 장기적인 차원에서 MFC를 따르는게 대세였던거 같다.

나는 그당시 두 라이브러리가 아닌 순수 C++ 클래스를 공부하라는 지적을 많이하고 했다..사실 나즌 요즘에서야 비로서 MFC를 뒤적거리고 있기도 하다.

프로그램을 하다 필요한 클래스가 혹시나 있을까 생각해서 사이트를 뒤지다 보면 솔직히 별로 쓸만한 소스를 찾는적은 거의 없던것 같다.
모든 클래들이 상속을 전제로 만들었다기 보다는 마치 하나의 함수묶음정도로 밖엔 생각이 안들정도의 소스뿐이기 때문이다.

클래스에서 가장 중요한 상속성을 고려하지 않고 하나의 클래스가 너무나 많은 기능으로 제작되어 실제 사용시 필요하지 않은 기능까지 너무 많이 나열되어 특정한 부분을 수정할려고 해도 내장 함수 상호연동상의 문제로 그 수정이 용의하지 않는게 대부분이다.

내가 보기는 MFC 처럼 상당히 거의 완성된 클래스만을 사용하다보니 이런 결과가 나오지 않았나 생각된다. 결국에 MFC든 OWL이 되든 객체지향적인 설계가 아니라 객체지향 라이브러리의 조합을 잘하는 모양이 된게 아닌가 생각한다.

객체지향 클래스 설계에 있어서 가장 중요한 점은 상속성 설계에 있다. 대부분 1단계 상속에만 익수하다보니 어떤 기능을 조상과 후손으로 발전할지가 정리하는게 쉽지많은 않다. 그러다 보니 최종 클래스를 미리 만들고 거기서 기능을 추려서 상위 클래스로 올라가는 꺼꾸로식 설계를 하는 개발자도 쉬 찾아볼 수 있다.

잘 설계된 클래스의 경운 약 3~5 단계의 상속으로 사용하도록 설계하는게 가장 효과적이라고 생각한다.

1단계는 대부분이 클래스들의 종류나 그 타입을 정의하는 최소한의 클래스를 위치한다. CObject 가 바로 그 것이다.

2단계의 경우는 큰 그룹으로 나뉘어 지게 된다.
세부 기능보다는 큰 줄기를 구성하는 형태가 될것이다.

보통은 I/O, Buffer, Memory, 등과 같이 입출력의 형태별로 쪼개지며 그 기초 함수들이 정의된다. open, read, wirte, close 등과 같이 모든 입/출력 클래스에서 필요로 하는 기본 함수들이 정의된다. 또한 Abstract 함수가 이 단계에서 정의 된다.

3단계의 클래스에서 비로소 그 특징적 성격이 클래스 변수형태로 추가되어 비로서 특정한 로직을 구현하는 클래스가 완성되며 이 단계에서 Virtual함수들이 이 단계에서 정의 된다.

4 단계에서는 사용자의 함수와 더블어 Abstract, Virtual함수의 재 사용성으로 설계를 한다.

보통은 4단계 정도면 대부분의 많은 기능이 다 구현되고 있지만 유지보수 또는 테스트 단계에서 5단계의 클래스를 만들어 이용하기도 한다.

클래스 설계시 두번쨰로 중요한건 바로 접근 범위의 설정이다.
private, protected, public, friend의 4가지 형태의 접근방법이 있다.

영어다 보니 정확한 이해가 안될수 있으니까 좀더 쉽게 설명하면
개인, 집안, 공개, 친구 이런식의 해석이 될수 있다.

개인(private)은 아버지와 아들간에서도 서로 숨겨야 될 성격으로 해당 클래스에서만 접근할 수 있는 영역이다.
집안(protected)는 상속된 클래스만이 접근할수 있으나 친구(friend)로 별도 정의된 영역에서도 그 내용을 볼수 있다.
공개(public)은 모든 영역에서 접근할수 있도록 형용한 영역이다.

보통 public 은 기존의 global변수와 동일한 수준이다 보니 가능하면 최소한의 한다는 정도는 다들 알고 있다.

private, protected의 경우가 개발자들이 별 구분없이 쓰다 보니 대부분 protected(생략시 기본) 과 public의 두개로 클래스가 구성된 경우가 많다. 크래스를 설계시 최대한 변수나 함수를 private로 많이 배치하는 습관이 중요하다. 그것이 상속된 프로그램에서 실수를 하지 않는 좋은 습관이다.

추상함수(Abstract)함수는 virtual void fun() = 0; 과 같은 식으로 정의하는 함수이며 상위 클래스 함수에서 호출할 예정인 함수로 그 내용은 바로 정의 하지 못할때 사용하는 함수로서 이 함수를 이용하게 되면 상속받을 클래스의 코딩시 꼭 필요한 기능에 대한 제한적 설계가 용의 하다. 이 함수를 잘 사용한다는것은 바로 클래스를 상속을 정확히 이해하며 상속단계별 그 용도에 대한 설계를 충분히 검토했다고 생각할 수 있다.

가상함수(Virtual)는 함수의 재사용에 대한 부분으로 추상함수보다는 좀더 확정성 측면을 고려된 함수다. 기본적인 기능이 구현된 상태에서 그 기능의 확장을 이용할 수 있도록 설계하는 방식으로 기능 추가및 확장성을 고려한다면 Virtual함수를 잘 사용해야 할것이다.

클래스 설계시 함수 또는 변수명은 개인별 취향이나 많은 문서에서도 나름대로 정의하고 있기는 하지만 가급적 간단하고 그 의미가 한정되지 않도록 하는것이 좋다.
open, close, read, write, send, recv, init, quit, set, get, count, print, 등과 같은 함수명들이 내가 주로 사용하는 함수들이다.

모든 클래스에서 위의 함수를 사요하게 되면 아마도 대부분의 기능을 다 구현할 수 있을것이다. 이미 클래스로 한정된 그룹이므로 함수나 변수 자체에 너무 의미를 주지 않고 작성하는게 좋다.
함수이름만으로는 그 동작의 의미가 충분이 이해할 정도의 이름을 붙이고 클래스 이름에 좀더 한정적인 이름을 붙이는것이 보다 효과적인 사용에 유리하다.

클래스를 처음 설계하다 보면 하나의 대형 클래스로 되어서 실제로 모든 함수가 Public이 되는 경우를 누구나 경험하게 된다.
클래스의 설계는 정말 많은 노력과 연습만이 그 단계를 정의가 가능하다고 생각된다.

list, queue, stack, array, container 는 프로그램어라면 한번쯤 코딩해 보는것을 추천하고 싶다.

포인터와 어드레스

C 언어를 공부하다보면 가장 많이 나오는 단어가 바로 포인터(*) 와 어드레스(&) 일거다.
이 두차이의 의미를 정확히 이해한다면 C 언어의 50% 정도는 이해했다고 해도 무방하다.

간단히 두 단어의 뜻을 정의한다면 포인트는 메모리주소를 담는 주소값이고 어드리세는 할당된 메모리주소를 담는 주소값이다.
어드레스는 변수 또는 함수의 절대 주소이며 그 값은 변동이 없다는 뜻이다. 하지만 포인트는 그 어드레스를 값을 담아서 변동하는 주소라고 생각하면 된다.

char *ptr = “1234567890”;
char str[10] = “abcd”;

이 두줄에서 나온 포인터와 어드레스는 정확히 몇개일까요?
이렇게 질문하면 아마도 포인터 변수 하나에 char 버퍼가 하나인데요라고 답을 할것이다. 꼭 틀린답은 아니지만 정확하다고 할 순 없다.
여기서 포인트는 1개 어드레스 2개가 정답이다.

“1234”, “abcd” 이 두개가 바로 어드레스 2개이다.
프로그램상에서 두 스트링 열이 스택상에 그 내용을 담고서 그 번지를 지정하고 있다. ptr 변수는 바로 “1234” 의 주소값을 담고 있는 변수이고 str 은 “abcd”로 초기화된 어드레스의 주소이다.

많은 프로그램어들이 자주 실수하는 버그 유형중에 하나가 포인터 변수의 사이즈를 잘목계산해서 발생하는 로직 오류다.
sizeof( ptr );
sizeof( str );
이두 줄의 결과는 4 와 10 이다. 여기서 sizeof의 의미는 해당 변수의 할당된 영역의 크기를 반환하는 함수다. ptr의 size는 어드리스(4바이트) 이므로 4이고 str의 size는 10개의 char 의 방을 잡고 있으므로 10인것이다.

char *a, int *b, long *c 이 세가지 변수의 sizeof의 결과는 모두 4를 반환하는 이유이다.

버퍼를 초기화 하는 방법에 memset을 많이들 사용한다.
memset( ptr, 0, sizeof(ptr) );
memset( str, 0, sizeof(str) );
이렇게 사용하게 되면 첫번째는 4바이트만 초기화가 된다.

포인터 변수가 혼동되는 이유중에 하나가 바로 표기상의 문제다.
char *p, char* p, char * p
띄어쓰기의 차이는 있지만 3가지 모두 같은 의미로 컴파일된다.
가장 정확한 표기는 char * p => (char *)p 이렇게 생각하는게 쉽다
즉 p는 char * 형태의 변수를 의미한다는것이다.
모든 포인트 변수는 4바이트의 주소값(프로세서에 따라서 가변)을 지정하기 때문에 모두 같은 사이즈가 된다.

char *a, int *b, long *c 의 주소가 모두 4바이트라면 타입의 의미는 어떻게 사용될까?
char는 1바이트, int는 2바이트, long은 4바이트라는 가정을 한다면
a = 100; b = 100; c = 100;

각 변수를 하나씩 증가하면 a++, b++, c++ 그때 값은 얼마일까?
a = 101; b = 102; c = 104; 이렇게 변해있을것이다.

바로 어드레스 증가시 사용될 offset을 의미한다.

숫자에서 가장 중요한 값은 바로 0 이다.
이와 대등한 수준에서의 변수 형태가 바로 void이다.
포인트 다음으로 아마도 자주 사용하면서도 정확히 이해가 어려운 타입이기도 하다. void 를 설명하자면 무형식의 형식이다.

void *a 라고 선언을 하게 되면 a 는 바로 어드레스를 담을 수 있는 변수는 되지만 a++ 를 할 수가 없다. 컴파일러가 얼마를 이동해야 할지 모르기 떄문이다. 하지만 모든 포이터 변수를 받아들이기 위한 값이 되기 때문에 아주 중요하게 이용되기도 한다.

함수가 변수 전달이나 함수를 전달하는 방식에서도 포인트와 어드레스는 다르게 사용된다.
함사를 호출하는건 해당 함수의 어드레스를 호출하는것이며 이때 변수를 넘겨주기 위해서 변수의 값 또는 그 변수의 주소를 직접 넘겨주는 방식으로 함수를 호출 할수 있다.

fun( int a ), fun( int *a ), fun( int &a ) 이렇게 3개지 형태로 함수를 정의 할 수 있다. 앞의 두가지가 C에서 사용하는 형식이고 3번째는 C++의 Call by Reference의 방법이라 약간은 다른 내용이다.

이 함수를 부는 방식은 fun( a ), fun( &a ), fun( a )이런식으로 각각 호출을 하게 될것이다.

첫번쨰는 함수 호출시 그 값을 넘겨주는것이면 두번째는 변수의 주소를 넘겨주는것이며 세번째는 복재의 의미로 사용되는 형태이다.

두번째 방식이 중요한 포인터의 내용이 된다.

int fun_a( int a ) { return a+1; }
void fun_b( int *a ) { *a++; }

void main()
{
int i = 10;
i = fun_a( i );
fun_b( &i );
}

두개의 함수는 거의 비슷한 결론을 만들어 내지만 그 호출 방법과 연산 방법이 상당히 다른걸 볼 수 있다.

프로그램에 있어서 포인트만을 가지고 코딩을 하고 1,2,3,4 차원의 배열과 포인트만으로 아주 복잡한 코딩을 고집할 필요는 없다. 하지만 포인트 자체를 무시하고 모든 함수를 작성하다보면 글로벌 변수가 많이 늘어나거나 함수의 인자가 복잡해 지는 결과가 나오게 된다.

정확한 이해를 통한 포인트의 사용많이 간결하면서도 효율적인 포인트 프로그램이 될것이다.

다음번에는 왜 자바엔 포인트가 없을까에 대해서 정리를 해볼까 한다.