Operating Systems : Three Easy Pieces를 보고 번역 및 정리한 내용들입니다.
컴퓨터 시스템에서의 보안은 갈 수록 더 중요해지고 있고, 컴퓨터 시스템의 구성 요소들은 모두 공격의 대상이 될 수 있다. 보안의 관점에서 볼 때 운영체제는 특히 더 중요하다. 그 이유는 뭘까?
그 이유로는 우선 (거의) 모든 것들이 운영체제 위에서 돌아가기 때문이다. 소프트웨어가 운영체제, 미들웨어, 혹은 그 외의 어느 것 위에서 돌아가든, 그 기반이 보안상 취약하면 그 소프트웨어도 취약해질 수 밖에 없다. 애플리케이션이 그 자체로는 보안 취약점을 가지고 있지 않다고 해도, 공격자는 그 기저의 소프트웨어를 공격함으로써 자신의 목적을 달성할 수 있기 때문이다.
이 부분은 운영체제에 특히나 더 중요하다. 특정 웹 서버, DB, 혹은 미들웨어 플랫폼의 경우, 그것들을 대체할 수 있는 것들도 많기 때문에, 어떤 것에 보안상 문제가 있다면 그것 대신 다른 걸 사용하면 된다. 하지만 OS는 사용하지 않을 수 없고, 그 중 어떤 것을 사용할지에 대한 선택지도 그리 많지 않다. 따라서 OS의 보안 취약점은, 특히 널리 쓰이는 것들의 경우, 사용자와 소프트웨어에 아주 큰 영향을 미친다. OS 보안이 중요한 또 다른 이유는, 궁극적으로 모든 소프트웨어가 프로세서, 메모리, 주변 장치 등, 기저 하드웨어의 적절한 동작에 의존하고 있으며, 이러한 하드웨어 자원을 제어하는 것이 바로 OS이기 때문이다.
현대 OS들은 크고 복잡하기 때문에, OS에 대한 보안 작업은 그리 쉬운 것이 아니다. 코드를 짜본 적이 있다면 알겠지만, 코드가 길어지고 알고리즘이 복잡해질 수록 코드에는 결함이 많아지기 쉽다. 소프트웨어 보안에서의 오류는 보통 이런 결함들 때문에 일어난다. 크고 복잡한 프로그램은 작고 간단한 프로그램보다 안전하기 어렵고, 현대의 운영체제만큼 크고 복잡한 프로그램은 거의 없다.
OS 보안에서 어려운 문제 중 하나는 현대 OS가 대부분 다수의 프로세스들을 동시에 지원한다는 점에 있다. OS에는 프로세스들을 서로 격리하고, 공유되는 하드웨어 자원들이 다른 프로세스들을 방해하지 못하도록 보호하기 위한 여러 메커니즘들이 사용된다. 만약 모든 프로세스가 다른 프로세스에 악영향을 주지 않음이 보장되어, 하드웨어 자원 및 데이터의 제한 없이 자신의 일을 무제한적으로 할 수 있도록 믿을 수 있는 것들이라면 OS의 보안 문제는 훨씬 더 쉬워질 것이다. 하지만 그럴 수는 없다.
OS 보안을 다른 관점에서 생각해보자. OS의 역할 중 하나는 응용 프로그램에 유용한 추상화를 제공하는 것이다. 애플리케이션들의 동작은 OS가 어떻게 이를 구현하고 있는지에 의존한다. 이 추상화에는 보안과 관련한 것들도 있으며, 애플리케이션들은 스스로가 원하는 보안 목적을 달성하기 위해 이에 의존한다. 만약 OS의 보안 관련 추상화가 제대로 구현되지 않아 믿을 수 없게 된다면, 애플리케이션은 이를 통한 보안 목표 달성에 실패할 수 밖에 없을 것이다.
OS 보안은 중요하지만 달성하기는 어렵다. 그렇다면 OS를 안전하게 만들기 위해서는 무엇을 해야할까? 여기에 대한 완벽한 답은 없지만, 적어도 몇개의 중요한 원칙과 도구들이 있다. 이것들은 보통 우리가 사용하는 범용 OS에 만들어져 있다. 보안 문제에 크게 관심이 없을지도 모르지만, 우리가 원하는 것들을 시스템이 어떻게 제공하는지를 이해하기 위해서는 OS가 자기 자신에 대한 보안을 어떻게 확보하는지를 이해할 필요가 있다.
보통 쓰이는 OS는 컴퓨터에 있는 모든, 혹은 거의 모든 하드웨어에 대한 전적인 제어를 가지고 있으며, 하드웨어가 허락한다면 말 그대로 무엇이든 할 수 있다. 여기에는 프로세서를 제어하고, 레지스터를 읽고 쓰고, 메인 메모리 위치를 찾고, 주변 장치가 지원하는 연산을 하는 것 등을 포함된다. OS가 할 수 있는 것들에는 다음의 것들이 있다.
근본적으로 프로세스는 OS는 프로세스에 크게 의존한다. 프로세스가 악의적인 OS로부터 자기 자신을 지키는 것은 거의 불가능하다. 보통은 OS가 악의적이지 않다고 간주한다. 하지만 여기에 어떤 결함이 있다면 악의적인 프로세스는 이를 이용해 OS가 가진 모든 권한을 이용할 수 있다면, 이는 악의적 OS나 마찬가지인 결과로 이어질 수 있다. 안전한 OS 설계가 중요해지는 이유다. OS의 보안 결함을 이용하면 시스템이 돌아가는 기기의 어느 것이든 조작할 수 있기 때문이다.
보안 연구자들이 정의한, OS를 포함한 시스템들에서 나타나는 세 가지 큰 보안 관련 목표에는 다음의 것들이 있다.
이 세 가지는 오직 허가된 사람, 혹은 그룹에만 특정 정보 및 행동을 허용하고, 그렇지 않은 사람, 혹은 그룹에는 허용하지 않을 것을 그 핵심으로 한다.
컴퓨터 시스템 보안에서 중요하게 다뤄야 할 문제 중에는 부인 방지(non-repudiation)도 있다. 이는 쉽게 말해 누군가가 처음에 말한 것을 나중에 번복하는 것을 허용하지 않는 것이다. 자신이 한 행동을 부인하는 일이 더 어렵고 복잡해지면, 사람들은 자신의 행동에 책임을 지니게 될 것이고, 따라서 악의적인 행동을 할 사람들도 줄어들게 될 것이다. 악의적인 행동을 하는 이는 결국 잡힐 것이고, 자신이 그러지 않았다고 할 수 없게 된다.
이것들은 크고 일반적인 목표들이고, 실제 시스템에서는 좀더 구체적이고 자세한 목표들을 찾아 내야 한다. 예를 들어 한 프로세스의 메모리 공간을 다른 프로세스들이 임의로 읽을 수 없게 하는 기밀성 목표가 있을 수 있다. 특정 파일에 레코드를 쓰는 경우, 그 파일에 쓸 권한이 없는 다른 사용자는 레코드를 쓰지 못하게 하는 무결성 목적도 가질 수 있다. 시스템 내의 한 프로세스가 CPU를 독점해 다른 프로세스들이 실행되지 못하게 하는 일을 방지하는 가용성 목적을 가질 수도 있다.
물론 특정 시스템들을 구현할 때에는 위 수준의 목표도 충분히 구체적이지 않을 수 있고, 따라서 이러한 보안 목표들을 달성하기 위해서는 좀 더 많은 세부 사항들을 명시할 수 있어야 한다. OS는 다양한 목적을 가진 다양한 사람들이 쓸 수 있도록 만들어지며, OS 보안 또한 그런 일반성을 반영할 수 있어야 한다. OS 보안 메커니즘에 필요한 것은 그 세부 보안 목표들을 충족하기 위해 필요한 유연성이다.
위와 같은 보안 목표들을 훨씬 더 구체적인 보안 정책들로 전환할 수 있어야 한다. 예를 들어 위의 무결성 목표에 대해, "사용자 A, B만이 파일 X에 쓸 수 있고, 나머지는 쓸 수 없다."라는 식으로 보안 정책을 구체화할 수 있다. 이 정도의 구체성에, 적절하게 설계되고 구현된 메커니즘이 뒷받침되어야만 보안 목표를 달성할 수 있다.
대부분의 경우 OS는 특정 보안 정책을 구현하기 위해 필요한 메커니즘들을 가지고 있다. 하지만 이러한 메커니즘의 사용은 정확한 보안 정책의 설정이 있고 나서야 의미를 가진다. 특별한 몇 가지 예외를 제외하고, OS는 많은 구체적인 정책들을 구현하는 데 필요한 일반적인 메커니즘을 제공할 뿐이다. 어떤 정책을 설계하고 어떻게 메커니즘을 적용시킬지가 중요하다.
시스템 설계 분야의 컴퓨터 과학자들의 경험에 따르면, 보안이 요구되는 시스템을 만드는 데 유용한 설계 원칙들이 있다. 물론 아래에 소개할 이 원칙들을 지킨다고 해서 시스템에 결함이 생겨날 확률이 완전히 사라진다는 것은 아니지만, 적어도 이것들에 대해 신경을 쓴다면 보다 안전한 시스템이 될 수는 있을 것이다.
위 보안 목표 중 일부는 OS 모델에 내장된다. 내장 목표는 아주 흔한 것들이나, 혹은 더 세부적인 목표들을 달성하기 위해 반드시 필요한 것들이다. 이 내장 목표들의 대부분은 프로세스의 하드웨어 접근을 제어하는 것과 관련되어 있다. 그 이유는 하드웨어가 시스템 내 모든 프로세스들이 공유하는 것이며, 만약 그러한 공유가 제대로 제어되지 못하면 그 중 한 프로세스가 다른 모든 프로세스의 보안 목표를 방해하게 될 수 있기 때문이다. 다른 내장 목표는 파일 시스템, 메모리 관리, IPC 등과 같이 OS가 제공하는 서비스들과 관련되어 있다. 이러한 서비스들이 제대로 제어되지 않으면, 프로세스들은 시스템의 보안 목표를 전복시킬 수도 있다.
시스템 보안의 대부분은 프로세스의 처리와 관련되어 있다. 만약 OS가 프로세스들을 깔끔하게 분리시키고 오직 OS만을 통해서만 통신할 수 있게 한다면, 공유되는 하드웨어나 OS 서비스를 이용해 시스템의 보안 목표를 전복시키는 일은 일어나지 않을 것이다. 따라서 OS는 하드웨어나 서비스 사용을 허용할 때 주의를 기울여야 한다.
시스템 콜 또한 OS의 보호를 위해 사용될 수 있다. 대부분의 운영체제에서 프로세스는 명시적인 시스템 콜을 통해 OS에 접근한다. 시스템 콜은 프로세서의 실행 모드를 사용자 모드에서 커널 모드로 바꾸고, OS에서 사용자가 원하는 서비스를 처리하는 코드를 실행하도록 한다. 이 코드는 어떤 프로세스가 시스템 콜을 호출했는지, 그리고 해당 프로세스가 요청한 서비스가 무엇인지를 알 수 있다. 앞에서는 이런 과정이 어떻게 이루어지는지에 대해서만 배웠지만, OS가 요청된 서비스를 시스템 보안 정책 아래에서 제공할 수 있는지를 확인하는 데에도 동일한 메커니즘을 사용할 수 있다.
프로세스가 시스템 콜을 수행할 때, OS는 프로세스의 PCB 등에 있는 PID를 이용해 프로세스를 식별한다. 이후 OS는 접근 제어 메커니즘을 통해 해당 프로세스가 요청된 액션을 수행하기 위한 권한이 있는지를 확인한다. 만약 권한을 가지고 있다면 OS는 프로세스를 대신해 해당 액션을 수행하거나, 혹은 추가적인 시스템의 개입 없이 프로세스가 작업을 수행할 수 있도록 준비한다. 만약 프로세스에 권한이 없다면 OS는 시스템 콜에 대한 에러 코드를 생성하고 제어를 프로세스에 제어를 돌려준다.
OS의 보안은 그 자체로도 중요하고, 애플리케이션에게도 중요하다. 시스템 보안을 달성하는 것은 어렵지만, 이에 도움이 되는 여러 설계 원칙들이 있다. 이 원칙들은 OS 뿐만 아니라, 다른 큰 소프트웨어 시스템들을 설계하는 데에도 쓰일 수 있다.
OS에서의 보안 달성은 보안 목표가 무엇이냐에 달려있다. 만약 이 목표들은 보통 기밀성, 무결성, 가용성과 관련되어 있다. 시스템에 따라 세부적인 보안 목표들은 달라질 수 있으며, 다른 시스템들은 그런 구체적인 보안 목표들에 맞는 서로 다른 보안 정책들을 가진다.