[OS] 42. Access Control

Park Yeongseo·2024년 4월 15일
1

OS

목록 보기
51/54
post-thumbnail

Operating Systems : Three Easy Pieces를 보고 번역 및 정리한 내용들입니다.

1. Introduction

보안 목표와 정책들이 정해지고, 서비스를 요청하는 사람이 누구인지에 대한 증거도 주어졌다고 하자. 이제는 그 정보들을 실제로 해당 정책을 수행하는 소프트웨어로 바꿔야 한다.

  1. 요청이 보안 정책에 들어맞는지를 살펴본다.
  2. 만약 그렇다면 연산을 수행하고, 그렇지 않으면 일어나지 않도록 보장해야 한다.

위의 1번 단계를 가리켜 접근 제어(access control)라 한다. 우리는 어떤 시스템 자원, 혹은 서비스가 누구에게, 어떤 환경에서 접근될 수 있는지를 결정해야 한다. 이러한 결정은 어떻게 만들어질 수 있을까?

2. Important Aspects Of The Access Control Problem

일반적으로 시스템은 이러한 결정을 내리기 위한 어떤 알고리즘들을 실행하는데, 이 알고리즘은 특정한 입력을 받아, 접근에 대한 yes-or-no 결정에 해당하는 아웃풋을 내놓는다. 높은 수준에서, 접근 제어는 주체(subject), 객체(object), 접근(access)의 세 용어를 통해 말해진다.

  • 주체 : 접근을 수행하는 엔티티. 보통은 사용자나 프로세스.
  • 객체 : 주체가 접근하고자 하는 대상. 파일이나 장치.
  • 접근 : 객체를 다루기 위한 특정 모드. 읽기, 쓰기 등.

접근 권한 결정은 특정 주체가 특정 객체에 대한 특정 접근을 수행하는 것이 허용되는지에 대한 것이라 할 수 있겠다. 이러한 결정 프로세스를 가리켜 인가(authorization)라고 부르기도 한다.

인증과 인가
인증 : 사용자의 신원을 검증하는 것.
인가 : 사용자에게 권한을 허용하는 것.

관련된 하나의 이슈로는, 이 접근 제어 결정이 언제 내려져야하는지에 대한 것이 있다. 시스템은 해당 결정이 내려질 때마다 어떤 알고리즘을 실행해야 하는데, 이 알고리즘을 구현하는 코드를 가리켜 참조 모니터(reference monitor)라 부른다. 이 알고리즘은 정확하면서도 효율적으로 구현되어야 하는데, 만약 정확하지 않으면 잘못된 접근 제어 결정을 내릴 것이고, 효율적이지 않으면 결정을 내릴 때 오버헤드가 발생할 것이기 때문이다. 보안 이득과 비용 사이의 균형점을 찾아야 한다. 하지만 보안에 있어서의 타협 없이도 적은 비용을 달성할 수 있는 특별한 케이스들을 찾을 수 있다면, (적어도 그 경우들에 있어서는) 더욱 좋을 것이다.

그렇게 하기 위한 한 방법은, 주체들에 자신에게만 속하는 객체들을 주는 것이다. 만약 객체가 원래부터 주체의 것이라면, 시스템은 주체의 해당 객체에 대한 접근을 무제한적으로 허용해도 괜찮을 것이다. 가상화가 이러한 종류의 가상 객체를 만들 수 있도록 하는데, 가상 메모리가 좋은 예다. 프로세스는 OS의 확인 없이 자신의 가상 메모리에 자유롭게 접근할 수 있다. 만약 이러한 가상화가 없다면, 프로세스가 메모리에 접근할 때마다 해당 접근을 검토해야 할 것이므로 시스템의 속도는 현저히 떨어지게 될 것이다.

이와 비슷한 가상화 기법을 주변 장치들에 대해서도 적용할 수 있다. 프로세스가 특정 가상 장치에 대한 접근을 얻고, 다른 프로세스들이 해당 장치에 접근할 수 없음이 보장된다고 하자. 물론 해당 장치에 대한 접근이 OS를 거쳐서 이루어져야 하는 것은 마찬가지지만, OS는 해당 프로세스가 장치에 접근할 때 접근 제어를 확인하지 않고 허용해도 괜찮다.

다만 가상화는 OS가 제공하는 환상이며, 프로세스들은 실제로는 메모리, 장치, 그리고 그외의 자원들을 공유한다. 프로세스는 그것들이 자기 자신만의 것이라고 생각하지만, 사실은 OS가 배후에서 그런 환상을 제공하고 있는 것이다. 따라서 이 경우는 사실 인가의 책임을 프로세스나 애플리케이션에서, 가상화 기능을 제공하는 OS로 옮기는 것에 불과하다고 할 수 있다.

이 문제 해결을 위해 고안된, 주로 쓰이는 방식에는 두 가지가 있다. 하나는 접근 제어 리스트(access control list, ACL)고, 나머지는 자격(capability)라 불린다. 자격은 주체에게 발급되는 것으로 열쇠, 티켓, 토큰과 같은 것이라 생각할 수 있다. 자격-기반 시스템에서, 주체는 특정 객체에 접근할 때 자신이 가지고 있는 해당 객체에 자격을 제출한다. 한편 접근 제어 리스트 시스템에서는, 시스템이 자신의 접근 제어 리스트에서 해당 객체한 허용 주체 목록을 확인한다.

이 두 방식은 어찌 보면 그렇게 다르지 않은 것처럼 보이지만, 이것들을 구현하기 위한 알고리즘과 자료 구조에 대해 생각해보면 크게 다른 점들을 볼 수 있다.

3. Using ACLs For Access Control

우선 접근 제어를 위해 ACL을 사용하는 경우를 살펴보자. 물론 시스템이 거대한 하나의 ACL을 가지고 있을 수도 있다. 하지만 이 경우, 주체나 객체가 너무 많아진다면 그 리스트 또한 너무 커지게 될 것이다. 그렇기 때문에 일반적으로 ACL을 사용하는 OS에서는 각 파일이 자기 자신의 ACL을 가지고, 이를 접근 제어에 이용한다. 그 과정은 다음과 같이 이루어진다.

  1. open() 시스템 콜이 호출되어 OS에 트랩이 발생한다.
  2. OS는 해당 시스템 콜을 호출한 프로세스의 PCB를 통해 누가 해당 프로세스를 소유하고 있는지를 확인한다. 이 자료 구조가 "사용자 X가 해당 프로세스를 소유하고 있다."라고 말하고 있다고 하자.
  3. 시스템은 open()으로 열려 하는 파일의 ACL을 확인한다. 이 ACL은 파일의 메타데이터로, 다른 메타데이터와 함께, 혹은 그 옆에 저장된다.
  4. 이 리스트에서 X를 찾는다. 만약에 리스트에 X가 없으면 X의 접근을 허용하지 않는다.
  5. 만약 리스트에 X가 있으면, X에게 허가된 권한에는 무엇이 있는지를 확인한다.
    • 만약 X가 해당 파일을 쓰기 위해 열었고, ACL에는 X에 대한 읽기 권한만이 허용되어 있다면, 시스템은 접근을 거부하고 프로세스에 에러를 반환할 것이다.

위와 같이 원리만 본다면, 그다지 복잡해 보이지는 않는다. 하지만 "악마는 디테일에 숨어 있다". 예를 들어, ACL이 영구적으로 저장되어야 한다면, 어디에 저장되어야 하는 걸까? ACL에 담긴 정보는 잘 변하지 않을 보안 정책에 기반한 것이므로, 아마 영구적으로 저장되어야 할 것 같다. 그런데 만약 이 정보가 캐싱되지 않았다면, 파일을 열 때마다 해당 정보가 들어있는 플래시 드라이브나 디스크에 접근해야 할 것이다. 앞서의 데이터 관련 섹션에서 다룬 것처럼 파일로부터 어떤 정보를 실제로 불러 올 때에는 여러 번의 읽기를 수행했어야 했다. 파일의 ACL을 불러 올 때에도 또 다른 읽기가 필요할까? 만약 그렇다면, 저장 장치의 어디에 ACL을 저장해야 더 빠르게 접근할 수 있을까? 이미 우리가 읽고 있는 것에 가깝거나, 혹은 그 일부라면 가장 좋을 것이다. 가능한 위치로는 파일의 디렉토리 엔트리, 아이노드 혹은 파일의 첫 번째 데이터 블럭 등이 있을 것이다. ACL이 해당 위치에 가깝게, 혹은 그 내부에 있다면 접근 시간을 최소화할 수 있을 것 같다.

여기서 이어지는 문제에는 리스트의 크기에 대한 것이 있다. 원리 상 특정 파일에 접근할 수 있는 사용자는 무수히 많을 수 있고, 이들에게 허용될 접근 방식도 다양하기 때문에, 이 접근 제어 리스트의 크기 또한 너무 커질 수 있다. 하지만 일반적으로 파일은 한 사용자가 소유하고, 이에 대한 접근도 해당 사용자를 포함한 몇몇에게만 허용된다. 때문에 리스트에 모든 사용자들을 담을 수 있도록 공간을 예약해 놓을 필요는 없다. 시스템 내에 많은 사용자들이 있다 하더라도, 그 대부분은 리스트에 들어가지 않을 것이기 때문이다.

물론 예외적인 경우로, (거의) 모든 사용자들에게 접근을 허용해야 하는 파일들도 있다. 예를 들면 lsmv와 같이 자주 쓰이는 실행 파일, 혹은 시스템에서 쓰이는 폰트 파일, 네트워킹을 위한 설정 파일 등이 있다.

이러한 예외 상황까지도 생각을 한다면, 파일 별 리스트에 큰 공간을 예약해놓는 방법은 낭비가 심하기는 하지만 확실하기는 하다. 하지만 이 리스트가 꽉 차있어 낭비가 별로 발생하지 않는 경우에도 문제가 있기는 마찬가지다. 현대 OS는 수천 엔트리의 리스트를 아주 빠르게 탐색할 수 있지만, 이런 탐색이 매 파일 접근마다 일어나야 한다면 큰 오버헤드가 발생하는 것은 불보듯 뻔하기 때문이다. 이 문제를 가변-사이즈 리스트를 이용함으로써 해결할 수도 있을 것이지만, 이 경우, 어떻게 이 메타데이터를 파일 시스템에 맞출 수 있을지는 또 다른 고민거리다.

다행히도 대부분의 환경에서는 오래전 Bell Labs Unix 시스템에서부터 내려온 레거시의 일부를 이용할 수 있다. 당시에는 영구 저장장치가 적고 비싸서, 각 파일에 대한 큰 ACL을 저장할 방법이 없었다. 초기 UNIX 설계자들은 각 파일의 ACL로 9비트 정도를 지불할 수 있을 것이라 생각했다. 물론 9비트는 부족했지만, 그들은 하드웨어 부족을 보충하기 위한 기발한 방법을 생각해냈다.

그들은 자신들이 신경 쓰는 접근 방식이 사실 읽기, 쓰기, 실행의 세 가지라는 것을 알아냈고, 이를 이용하면 엔트리가 세 개만 있는 접근 제어 리스트로 대부분의 보안 정책을 처리할 수 있을 것이라 생각했다. 물론 각 엔트리마다 접근 모드 별로 1비트를 사용하면 9비트를 모두 써버리게 될 것이고, 각 엔트리가 누구에 해당하는 것인지는 기록할 수 없게 될 것이다(9비트 -> 엔트리 3개, 각 엔트리 당 3비트 -> 각 접근 모드 별 1비트, 총 3비트).

//3개의 엔트리를 가지는 9비트 ACL.
rwx => 누가?
rwx => 누가?
rwx => 누가?

이들은 ACL을 세 그룹으로 나눠 이 문제를 해결했다. 첫 번째 그룹은 파일의 소유자로, 그 신원은 아이노드에 저장되어 있다. 다른 하나는 특정 그룹의 멤버로, 해당 그룹의 ID 또한 아이노드에 저장되어 있다. 마지막 남은 하나는 이에 해당하지 않는 나머지 모두에 대한 것으로, 이를 통해 나머지 사용자들에 대한 각각의 엔트리를 만들지 않고도 그들이 가질 권한을 지정할 수 있게 된다.

//3개의 그룹을 엔트리로 갖는 9비트 ACL
rwx => 파일 소유자의 권한(아이노드에 저장) 
rwx => 특정 그룹의 권한(아이노드에 GID 저장)
rwx => 나머지 사용자들의 권한

이를 통해 다음의 문제들이 해결되며, 이러한 이점들로 인해 여전히 POSIX-호환 파일 시스템에서는 이 로직이 쓰이고 있다.

  • 저장소 크기 문제
  • 접근 및 확인 비용 문제
    + 이 파일에 접근하기 위해 이미 이 파일의 아이노드에 접근했을 것이고, ACL이 아이노드에 들어가 있다면 이를 얻기 위한 추가적인 탐색 및 읽기가 필요하지 않다.
  • 임의 사이즈의 리스트를 탐색할 때 생기는 오버헤드도 발생하지 않음.
    + 이 짧은 비트를 통해서 접근 제어 물음에 대해 답할 수 있게 됨.

물론 이 방식에는 한계도 있는데, 복잡한 접근 모드와 공유 관계등을 표현할 수는 없기 때문이다. 그 이유로 몇몇의 현대 시스템들은 좀 더 일반화된 ACL을 허용하는 확장된 방식을 사용하기도 하지만, 대부분의 경우 UNIX 스타일의 9비트 ACL를 쓴다.

이제 ACL을 사용했을 때의 장단점들에 대해 알아보자.

장점

  1. 누구에게 자원 접근이 허용되는지 알고 싶은 경우, ACL을 보기만 하면 된다.
  2. 객체에 접근할 수 있는 주체를 바꾸고 싶은 경우 ACL만 바꾸면 된다.
  3. ACL이 파일 내부, 혹은 적어도 그 근처에 저장되므로, 파일에 접근할 수 있으면 관련 접근 제어 정보도 쉽게 가져올 수 있다.
    • 이는 분산 시스템에서 특히 중요핟. 시스템의 성능 향상에도 영향을 준다.

단점

  1. 앞서 언급된 문제들을 해결할 수 있어야 함.
    • 파일 저장 문제, ACL 크기 문제, 탐색 문제
  2. 특정 주체에게 허용된 모든 자원들에는 무엇이 있는지 검색하려는 경우, 시스템의 모든 ACL들을 확인해야 함.
  3. 분산 환경에서, 다수의 서로 다른 컴퓨팅 도메인에서의 사용자 네임스페이스 일관성 문제를 해결해야 함.
    • A에서 B에 저장된 파일에 접근하려 한다고 해보자. 이때 B는 자신이 가지고 있는 ACL을 통해 A가 제공한 신원의 접근 제어를 확인해야 한다. 그러려면 A가 제공한 신원의 사용자가 B가 가지고 있는 사용자 정보가 일치함이 보장되어야 한다.

4. Using Capabilities For Access Control

ACL이 접근 제어를 위한 유일한 선택지는 아니다. ACL처럼 자격 또한 컴퓨터 시스템 내 긴 역사를 가지고 있다. 자격-기반 시스템에서, 실행 중인 프로세스는 자신의 접근 권한을 명시하는 자격 집합을 가지고 있다. 만약 ACL을 전혀 사용하지 않는, 순수한 자격 시스템을 사용한다면, 이 집합이 해당 프로세스의 모든 접근 권한을 인코딩한 전부다. Linux나 Windows에서는 이런 방법을 사용하지 않지만, Hydra 같은 몇몇 OS에서는 이 방식을 이용해 접근 제어를 다룬다.

그렇다면 순수 자격 시스템에서는 open() 콜은 어떻게 수행될까? 이 시스템 콜이 호출될 때에는, 애플리케이션이 해당 파일을 열 수 있도록 허락하는 자격을 파라미터로 제공하거나, 혹은 OS가 해당 자격을 찾아낸다. 두 경우 모두에서 OS는 해당 파일의 열기 수행을 허락할지 말지를 확인한다. 만약 허락된다면 OS는 파일을 열어주고, 그렇지 않으면 프로세스가 열 수 없는 파일이라는 메시지와 함께 에러를 반환할 것이다. 다만 이는 Linux에서 일어나는 일은 아님을 명시하자. Linux는 자격이 아닌 ACL을 사용한다.

여기서 생겨날 수 있는 몇 가지 의문점들이 있다. 자격이 대체 정확히 뭘까? OS는 자격의 유효성을 어떻게 확인할까? 자격은 제일 먼저 어디서부터 오는걸까?

자격은 컴퓨터 내 다른 정보들과 마찬가지로, 비트의 다발, 데이터다. 보호되어야 할 자원들이 많다는 것을 생각하면, 자격은 자원에 대한 구체적인 정보를 담고 있어야 하며, 꽤 길 것이고, 또 꽤 복잡할 것이다. 하지만 궁극적으로는 비트에 불과하다. 이전 장에서 이야기한 것처럼, 누구나 자신이 원하는 어떤 비트든 만들 수 있고, 프로세스가 생성할 수 없는 독점적인, 혹은 예약된 비트 패턴은 존재하지 않는다. 또한 만약 프로세스가 특정 비트 집합의 복사본을 하나라도 가지고 있다면, 더 많은 그 복사본을 만드는 것은 아주 쉬운 일이다. 누구나 자신이 원하는 비트들을 만들 수 있다는 것은, 곧 누구나 자신이 원하는 어떤 자격이든 만들 수 있다는 것을 함축한다. 또한 누구든지 자신이 가지고 있는 비트의 복사본을 만들 수 있다는 것은, 동작하는 자격을 가지고 있는 누구든, 그 복사본을 자신이 원하는 만큼 얼마든지 만들 수 있고, 잠재적으로는 자신이 원하는 어느 곳에든 저장할 수 있다는 것을 함축한다.

보안의 관점에서 보면 이런 특징들은 별로 좋아 보이지 않는다. 특정 파일을 열기 위해 프로세스가 특정 비트 패턴의 자격을 필요로 할 때, 이 비트 패턴은 그저 생성되어 성공적으로 원하던 파일에 접근할 수 있게 만들어주는 것일 수 있다. 이는 우리가 "자격"에 대해 기대하는 바가 아니다. 우리가 원하는 자격은 위조될 수 없는 것이어야 한다. 이 문제가 어떻게 해결된다고 하더라도, 자격을 복사할 수 있다는 것은 한 번 부여된 자격을 무효화하는 일을 어렵게 만든다. 프로세스는 해당 자격을 복사해놓고, 자신이 원하는 어딘가에 그 복사본을 치워놓으면 되기 때문이다. 더욱이, 프로세스는 IPC를 이용해 다른 프로세스로 자격 복사본을 전송함으로써 다른 프로세스에 접근 권한을 부여할 수도 있다.

이러한 이슈들은 보통, 프로세스가 직접 자격을 다루지 않고 OS를 통해서 다룰 수 있도록 만듦으로써 처리된다. OS는 자격을 자격들을 자신의 보호된 메모리 공간에 저장함으로써 제어하고 유지한다. 프로세스는 자격을 이용해 여러 연산들을 수행하지만, 이는 OS의 중재를 통해서만 가능해진다. 예를 들어, 프로세스 A가 B에게 특정 파일에 읽기/쓰기 접근을 허용하려고 할 때, A는 B에게 적절한 비트 패턴만을 보낼 수는 없다. 대신 A는 OS에 B에게 적절한 자격을 주라는 시스템 콜을 만들어야 한다. 이는 OS에게 자신의 보안 정책을 확인하도록 해, B에게 해당 파일에 대한 접근을 줄지, 혹은 그렇지 않을지를 결정하게 만든다.

따라서 접근 제어에 자격을 쓰고 싶다면, OS가 각 프로세스에 대한 보호된 자격 리스트를 유지할 수 있도록 해야 한다. 이는 간단한데, OS가 이미 프로세스 별로 보호된 자료 구조, PCB를 가지고 있기 때문이다. 커널 메모리에 저장된 자격 리스트의 포인터를 프로세스의 PCB에 추가하기만 하면 된다. 이제 프로세스가 특정 파일에 접근하려 할 때, 시스템 콜이 OS에 트랩을 발생시키고, OS는 해당 프로세스에 대한 자격 리스트를 보고 작업에 필요한 관련 자격이 있는지를 확인한 후, 그에 따라 작업을 진행한다.

일반적인 시스템에서 주체가 접근할 수 있는, 말 그대로 모든 자격 리스트를 유지하는 것은 많은 오버헤드를 일으킨다. 만약 자격을 파일-기반 접근 제어를 위해 사용한다면, 사용자는 접근이 허용된, 수 천개의 각 파일들에 대한 자격을 가지게 될 것이기 때문이다. 그렇기 때문에 일반적으로, 자격을 사용할 때 시스템은 자격을 어딘가 안전한 곳에 영구적으로 저장하고 필요할 때마다 가져와서 사용한다. 따라서 프로세스에 추가되는 자격 리스트는 그렇게 길 필요가 없다. 하지만 이 경우 각 실행되는 프로세스에 사용자가 가진 어떤 자격을 줄지를 결정해야 한다는 이슈가 있기는 하다.

다른 선택지도 있다. 자격을 OS에 저장하지 않고, 암호화로 보호하는 것이다. 만약 자격이 상대적으로 길고 강력한 암호화 기법으로 만들어진다면, 이것을 추측하는 것은 실제로는 불가능하기에 OS가 아닌 사용자가 직접 관리할 수도 있다. 암호화 자격은 분산 시스템에 더 잘 어울리기에, 분산 시스템 보안에 대한 다음의 장에서 다루게 될 것이다.

ACL에서와 마찬가지로 자격에도 여러 장단점이 있다.

장점

  1. 주체가 접근할 수 있는 시스템 자원에 무엇이 있는지를 확인하기 쉽다.
    • 주체가 가진 자격 리스트를 확인하면 된다.
  2. OS가 자격에 대한 독점적 접근권을 가지고 있다면, 접근 권한을 뺏는 일도 쉽다.
    • 단, 사용자가 관리하는 경우에는 어려워진다.
  3. 이미 메모리에 자격 정보가 있는 경우, 확인하기도 쉽다.
    • 자격이 자신이 보호하는 자원과 연관된 데이터, 혹은 소프트웨어로의 포인터를 포함할 수 있기 때문이다.

단점

  1. 특정 자원에 누가 접근할 수 있는지를 확인하는 일은 어려워진다. 이를 위해서는 모든 자격 리스트를 확인해야 하기 때문이다.
  2. Unix가 짧은 ACL을 개발해낸 것과 달리, 짧고 관리하기에 용이한 자격 리스트를 만드는 간단한 방법이 잘 개발되어 있지 않다.
  3. 시스템이 위조 문제가 일어나지 않게 자격을 만들고, 저장하고, 탐색할 수 있어야 하는데, 이는 어렵다.

자격은 제한된 권한을 가지는 프로세스를 생성하기 위한 좋은 방법을 제공한다는 데에서도 장점을 가지고 있다. ACL의 경우, 프로세스가 부모 프로세스의 신원을 상속하고, 해당 주체의 권한들을 상속한다. 프로세스에게 부모가 가진 권한의 일부분만을 주는 일은 어렵다. 이를 위해서는 제한된 권한을 가지는 새 주체를 만들고, ACL을 변경하고, 새 프로세스의 신원을 해당 주체로 변경하거나, 혹은 기존 방식과는 조금 다르게 동작하도록 접근 제어 모델을 확장해야 한다. 하지만 자격은 이 일을 쉽게 해낸다. 부모가 자원 X, Y, Z에 대한 자격을 가지고 있고, 자식 프로세스에게는 X, Y 자격만을 가지게 하고 싶다 해보자. 부모는 자식에게 X, Y 자격만 전송하면 된다.

실제로는, 사용자에게 보이는 접근 제어 메커니즘의 경우는 많은 이유로 자격이 아닌 ACL을 사용한다. 하지만 보이지 않는 곳에서 OS는 자격을 많이 사용한다. 예를 들어, 전형적인 Linux 시스템에서 open() 시스템 콜에서는 ACL을 사용한다고 했다. 하지만 open()이 성공하고 프로세스가 파일을 열어놓고 있는 동안 일어나는 후속 읽기/쓰기 작업에 대해서는 ACL 검사가 일어나지 않는다. 그 대신 Linux는 프로세스가 해당 파일에 대한 읽기/쓰기 권한을 가지고 있다는 자격과 비슷한 자료 구조를 생성한다. 이 자료 구조는 프로세스의 PCB에 추가된다. 이후의 읽기, 또는 쓰기 작업에서 OS는 파일의 ACL을 찾지 않고 이를 통해 해당 프로세스가 그 권한을 가지고 있는지의 여부를 결정한다. 만약 파일이 닫힌다면, 이 자격 비스무리한 구조는 PCB에서 삭제되고 프로세스는 다른 open()을 호출해 ACL을 재확인하지 않고는 해당 파일에 접근할 수 없게 된다. 비슷한 테크닉이 하드웨어 장치와 IPC 채널에도 쓰이는데, Unix 계열의 시스템에서는 이러한 자원들도 파일처럼 다루기 때문이다.

이렇게 혼합된 사용 방식을 이용하면 각 메커니즘에서 일어났던 문제들을 확인할 수 있다. 우선 각 연산마다 ACL을 확인하면서 발생한 비용이 절감된다. OS의 자료 구조 내에 포인터가 있는지의 여부만을 확인하면 되기 때문이다. 접근 가능한 모든 객체에 대한 자격을 관리하는 데 드는 비용도 절감된다. ACL 확인이 성공적으로 일어난 후에야 자격이 만들어지기 때문이다. 프로세스가 객체에 접근하지 않으면 ACL은 확인되지 않고 자격도 필요하지 않다. 또한 일반적으로 프로세스는 자신이 접근할 수 있는 파일들 중 극히 일부만을 열기 때문에, 확장성 이슈도 크게 발생하지 않게 된다.

5. Mandatory And Discretionary Access Control

컴퓨터 자원에 대한 접근 제어가 어떻게 되어야 하는지를 결정하는 건 누굴까? 답은 명확해 보인다. 바로 자원의 소유자다. 사용자의 파일의 경우 사용자가 접근 제어 설정을 결정하고, 시스템 자원의 경우 시스템 관리자, 혹은 컴퓨터의 소유자가 결정할 것이다. 하지만 어떤 시스템의 경우, 그리고 어떤 보안 정책의 경우, 이것은 정답이 아니다. 특히 정보 보안에 더 많은 신경을 쓰는 사람들은 이보다 더 엄격한 제어를 원한다.

군사 정보가 대표적인 예다. 극비 정보가 있다고 했을 때, 이는 누군가에게는 허용되지만 다른 이에게는 허용되지 않아야 한다. 이 정보가 어떤 파일에 담겨 있다고 했을 때, 누가 파일을 볼 수 있고 누가 파일을 볼 수 없는지를 결정하는 것은 기관 내의 정보 보안 책임자가 결정해야 한다. 이 주체가 다른 사용자들에 의해 만들어지고 그들에 속하는 정보 접근 제어를 설정할 권한을 가지고, 그 사용자들은 이 정보 보안 책임자의 결정을 변경할 수 없어야 한다.

더 흔한 경우는 임의 접근 제어(discretionary access control, DAC)다. 여기서는 거의 모두에게 자원에 대한 접근 권한을 줄지, 혹은 대부분에게 주지 않을지가 자원 소유자의 재량에 달려있다. 또는 강제적 접근 제어(mandatory access control, MAC)도 있다. 여기서는 접근 제어의 일부가 정보 소유자의 접근 권한 또한 변경할 수 있는 관리자에 의해 결정된다. DAC를 쓸지, MAC를 쓸지의 여부는 ACL, 자격, 혹은 그 외의 접근 제어 메커니즘 중 어떤 것을 쓸 것인지와는 독립적이며, MAC 시스템에 DAC의 요소를 포함시킬 수도 있다.

대부분의 사람들은 MAC를 이용하는 시스템을 사용하지 않기 때문에, 그에 대한 이야기는 여기까지 하자.

6. Practicalities Of Access Control Mechanisms

대부분의 시스템은 사용자들에게 간단하거나 더 강력한 ACL 메커니즘만을 노출시키고, 그 대부분은 DAC를 사용한다. 하지만 현대 컴퓨터가 수십만에서 수백만의 파일들을 가지고 있다는 것을 생각하면, 사람인 사용자들이 개인적으로 접근 제어 권한을 설정할 수 있도록 하는 것은 실현 불가능하다. 일반적으로 시스템은 각 사용자가 파일을 만들 때마다 사용될 기본 접근 권한을 설정할 수 있도록 한다. 만약 Linux open() 콜로 파일을 만드려 한다면, 이 시스템 콜을 호출하는 사람은 처음에 해당 파일에 할당될 접근 권한을 명시할 수 있다. Unix나 Linux 시스템에서 새롭게 만들어지는 파일에 대한 접근 권한은 umask() 콜을 통해서도 제어될 수 있는데, 이는 파일을 만드는 프로세스의 모든 새 파일 생성에 적용된다.

원한다면 소유자는 초기 ACL을 바꿀수도 있지만, 경험상 그런 경우는 적다. 그렇기 때문에 기본값을 적절히 선택하는 것이 중요하다. 이론적으로는 이를 변경하거나 조정하는 것이 가능하지만, 실제로는 대부분 변경되지 않은 채로 사용된다. 하지만 비록 이렇게 대부분의 경우는 기본값을 그대로 사용하더라도, 특정한 사용자 및 시스템들은 자신의 보안 목적 달성을 위해 이러한 종류한 제어를 필요로 한다. 예를 들어 많은 소프트웨어 설치 패키지들이 그들이 만드는 실행 및 설정 파일들에 접근 제어를 설정하는 경우가 그렇다.

보안 정책을 구현하기 위해 표준 접근 제어 방식을 사용할 때 발견된 한 가지 실제 이슈는, 조직 내 서로 다른 역할(role)을 가진 사람이 서로 다른 권한을 필요로 한다는 것이었다. 이러한 역할에 기반해 접근 제어를 구성하고, 특정 사용자들에게 역할을 할당하는 것은 많은 보안 정책 구현을 쉽게 만든다. 이러한 방식은 특히 특정 사용자가 현재 수행 중인 작업에 따라 역할을 바꿀 수 있을 때 가치가 있는데, 이 경우 접근 권한을 개인별로 변경하지 않고, 그 권한을 가지는 역할만 변경하면 되기 때문이다. 사용자는 해당 역할을 수행할 때에만 그 역할의 권한을 가지게 되며, 역할을 잃으면 그 권한도 잃게 된다.

이러한 관찰은 역할 기반 접근 제어(role-based access control, RBAC)로 이어졌다. 오늘날 RBAC는 여러 조직, 특히 큰 조직에서 흔히 쓰이고 있다. 큰 조직은 작은 조직에 비해 관리에 많은 어려움을 겪으며, 사용자 그룹을 한 번의 연산으로 다룰 수 있도록 하는 RBAC와 같은 방식은 관리에 많은 이점을 가져다 주기 때문이다.

RBAC는 ACL에서 사용했던 사용자 그룹과도 비슷해 보인다. 하지만 RBAC는 단순한 그룹 접근 권한보다는 훨씬 더 강력하다. 실제 세계에서 사용자는 필요한 작업에 따라 다양한 역할을 가질 수 있는데, 각 역할에 서로 다른 접근 권한을 부여한다면, 특정 작업에 필요한 접근 권한을 오용하는 경우를 방지할 수 있다. RBAC 시스템에서는 특정 사용자가 여러 개의, 서로 겹치지 않는 역할을 가질 수 있게 하고, 필요할 때 해당 사용자의 역할을 전환함으로써 접근 권한을 얻고 버릴 수 있도록 한다.

이러한 시스템에서는 종종 RBAC 역할에 대한 새로운 인증 단계도 필요하고, 한 역할에서 다른 역할로 옮겨 갈 때 이전 역할에 주어진 권한들을 포기할 수 있게도 만들어야 한다. 또한 RBAC에서는 보안 컨텍스트에 따라 단순한 파일 읽기/쓰기 정도 이상의, 더 미세한 구분을 제공해야 할 수도 있다. 예를 들어 판매원 역할이 특정 제품의 구매 내역을 파일에 쓸 수 있다고 하자. 이 판매원은 재입고는 하지 않기 때문에, 같은 제품과 같은 파일이라 하더라도 해당 제품에 대한 재입고 내역은 파일에 쓸 수 없어야 한다. 이렇게 특정 객체에 대한 세부 접근 규칙을 가리켜 객체의 보안 컨텍스트(security context)라 하며, 이런 보안 컨텍스트를 바탕으로 주체가 해당 객체에 접근할 수 있는지를 처리하는 메커니즘을 가리켜 type enforcement(TE)라 부른다.

Linux, 혹은 그와 비슷한 OS의 ACL과 그룹을 이용해 미니멀한 RBAC 시스템을 제작할 수도 있다. 이러한 시스템의 접근 제어 메커니즘에는 권한 승격(privilege escalation)이라 부르는 기능이 있다. 권한 승격은 보통 특정 프로그램이 해당 프로그램을 호출한 사용자가 가지는 권한과 상관 없는 권한 집합을 가지고 실행될 수 있게 한다. Unix와 Linux 시스템에서 이러한 기능은 setuid라 불린다. 이는 프로그램이 프로그램을 실행하는 사용자는 일반적으로 가질 수 없는 권한을 가지고 있는 다른 사용자의 권한으로 실행될 수 있도록한다. 하지만 이러한 권한들은 프로그램이 실행될 떄에만 주어져야 하고, 프로그램이 종료되면 사라져야 한다. 또한 setuid 프로그램은 해당 권한들을 사용하는 제한된 작업 집합만을 수행하도록 작성해, 그 권한들을 남용하는 일이 없도록 해야 한다. 간단한 RBAC 시스템은 각 역할에 맞는 사용자와, 각 사용자에 필요한 권한들을 정의함으로써 만들 수 있고, 이 권한들은 setuid를 통해 각 사용자에게 부여될 수 있다.

Linux의 sudo 커맨드 또한 사용자가 다른 신원으로 프로그램을 실행할 수 있도록 기능을 제공한다. 예를 들어

sudo -u Programmer install newprogram

install 커맨드를, 커맨드를 실행한 사용자의 신원이 아니라, 사용자 Programmer의 신원으로 실행한다. 다만 이 방식을 안전하게 사용하려면 누가 어떤 프로그램을 어떤 신원에서 실행할 수 있는지를 제어하는 시스템 파일 설정에 주의해야 한다. 보통 sudo 커맨드는 다른 RBAC에서와 같이 새로운 인증 단계를 필요로 한다.

RBAC 시스템은 보통 setuidsudo가 제공하는 것보다 더 미세한 구분 및 역할 할당 추적을 지원한다. 그런 RBAC 시스템은 OS의 부분일 수도 있고, 혹은 시스템 또는 프로그래밍 환경에 추가되는 방식일 수도 있다. RBAC를 쓸 때는 종종 어느 정도의 MAC도 실행한다. 그렇지 않다면 위 sudo의 예에서, Programmer의 신원으로 실행하는 사용자는 해당 커맨드를 이용해 파일의 접근 권한을 변경하고, Programmer가 아닌 사용자도 install 커맨드를 실행할 수 있도록 만들 수 있다. MAC를 이용하면 사용자는 Programmer 역할을 가지고 해당 커맨드를 실행할 수 있지만, 이를 가지고 다른 사용자에게 권한을 부여하는 일은 할 수 없게 된다.

7. Summary

ACL과 자격은 대부분의 접근 제어 시스템에서 쓰이는 가장 기초적인 메커니즘들이다. ACL은 어떤 주체가 어떤 객체에 어떤 방식으로 접근할 수 있는지를 정확히 명시하며, 해당 리스트에 있는지의 여부가 권한이 주어져도 되는지를 결정한다. 자격은 자물쇠의 열쇠와 같은 방식으로 동작한다. 올바른 자격을 가지고 있으면 그것만으로 자원에 접근할 수 있다.

사용자에게 보이는 접근 제어는 보통 ACL의 방식으로 이루어지지만, OS는 그 아래에서 자격도 사용한다. 둘 중 어느 것 하나가 다른 것보다 본질적으로 낫다고는 할 수 없고, 주어진 상황에 어느 것이 더 잘 어울리느냐가 더 중요하다.

접근 제어 메커니즘은 임의적일 수도 있고, 강제적일 수도 있다. 어떤 시스템은 둘 다이기도 하다.

접근 제어 메커니즘이 완전히 정확하고 극도로 효율적이라 하더라도, 이는 주어진 보안 정책을 구현하기 위해 쓰이는 것일 뿐이다. 잘못된 접근 제어 메커니즘으로 인한 보안 실패는 드물며, 대부분은 잘못 설계된 보안 정책 때문인 경우가 많다. 여기서도 마찬가지로, 보안 정책을 제대로 세우는 것이 중요하다.

이제 한 장 남았다.

2개의 댓글

comment-user-thumbnail
2024년 4월 15일

N-2번째 포스트 잘 읽었습니다 박영서 화이팅!

1개의 답글