Information Security
30 July 2012

Введение в безопасность на основе мандатных ссылок (англ. Capability-based security)

From Sandbox
В большинстве сегодняшних операционных систем модель безопасности своими корнями уходит в Unix, наследуя предположение о том, что пользователь может доверять программам, которые он запускает. Однако, как показывает практика – это утверждение в корне неверно.

Marc Stiegler сравнивает это с ситуацией, когда вы нанимая уборщика даете ему Главный И Единственный Ключ, которые не только открывает комнату, в которой необходимо сделать уборку, а открывает все двери, в т.ч. и дверь сейфа с золотом. Т.е. у вас всего две альтернативы – либо не давать уборщику этот ключ – и тогда он не сможет выполнить свою работу, либо дать и надеяться на его порядочность.

Модель безопасности на основе мандатных ссылок (англ. capability-based security) предлагает решение этой проблемы.

Модель руководствуется принципом минимальных привилегий. Например, открывая файл в текстовом редакторе, вы даете программе-редактору доступ только к одному конкретному файлу. Вы по прежнему не можете быть уверенны в том, что программа будет редактировать файл так, как вы этого ожидаете, а не зашифрует и потребует денег за расшифровку – но ставки теперь ощутимо ниже.

По-умолчанию, любая программа (а в идеале – любой кусок кода) выполняется в идеальной песочнице – у нее нет доступа ни к файловой системе, ни к сети, ни к другим программам. В некоторых реализациях даже нет права выделять память и потреблять ресурсы процессора!

Выйти за пределы песочницы программа может с помощью мандатной ссылки (англ. capability, не путать с POSIX Capabilities) на внешние ресурсы.

На сегодняшний день русскоязычной литературы по этой теме попросту нет – настолько нет, что для написания этой статьи перевод для термина “capability” мне пришлось изобретать самому. Итак, знакомитесь:

Мандатная ссылка – это особая форма ссылки, которая во-первых идентифицирует объект (и поэтому «ссылка»), а во-вторых определяет допустимые над ним операции (и поэтому «мандатная»). Могут сосуществовать мандатные ссылки, которые ссылаются на один и тот же объект, но задают разные наборы допустимых операций.

Тот, кто владеет мандатной ссылкой, всегда может выполнять любые операции над объектом, которые разрешены этой ссылкой – дополнительных проверок при выполнении операций не делается. Все проверки делаются перед тем, как субъект получит мандатную ссылку – если он ее получил, то это служит доказательством того, что все проверки субъектом пройдены.

Для сравнения, путь к файлу в виде строки идентифицирует объект, но не определяет допустимые над ним операции – поэтому каждый раз когда выполняется операция над файлом заданным путем, ОС должна выполнить проверку прав доступа.

С другой стороны, дескриптор открытого файла может считаться мандатной ссылкой. Проверка прав доступа делается перед тем, как дескриптор будет создан, и режим, в котором был открыт файл, сохраняется в структурах данных связанных с дескриптором.

В литературе по безопасности на основе мандатных ссылок можно встретить термины «ресурс» и «субъект» (PDF). «Ресурс» — это роль, которую играет объект, на который указывает мандатная ссылка. «Субъект» — это тот, кто имеет мандатную ссылку и может выполнять операции над ресурсом. Между субъектом и ресурсом существует связь многие-ко-многим: каждый субъект может иметь произвольное число мандатных ссылок на разные ресурсы, на один ресурс может ссылаться произвольное число субъектов.

В некоторых реализациях «субъект» и «ресурс» это роли, которые одни и те же объекты могут играть в зависимости от контекста. В других — это разнородные объекты, но разновидностью ресурса является канал общения с другим субъектом. Так или иначе, объекты могут общаться между собой с помощью мандатных ссылок – объект владеющий ссылкой может послать ссылаемому объекту сообщение, как в ООП. В дальнейшем для удобства изложения будем считать что объекты однородны – т.е. «субъект» и «ресурс» это не более чем ситуативные роли.

Модель предполагает что внутреннее состояние объектов инкапсулировано и не может быть изменено иначе как через посылку сообщения.

Движение мандатных ссылок


Объект может получить мандатную ссылку ограниченным числом способов:
  1. Как часть начальных условий – при инициализации системы (например, установке ОС) создается некоторый начальный граф объектов, который в дальнейшем развивается по описанным здесь правилам, однако начальный граф может создаваться «читерскими» методами.
  2. При создании нового объекта – операция создания нового объекта возвращает мандатную ссылку с максимальным объемом доступных полномочий.
  3. В приданное – создавая дочерний объект, родитель может передать ему часть доступных ему полномочий (передать в аргументах конструктора в терминологии ООП)
  4. Через общих знакомых – если у Алисы нет ссылки на Боба, то Керол может (если захочет!) дать ей эту ссылку. Для этого, в терминологии ООП, Керол посылает Алисе сообщение, передав ссылку на Боба как параметр.
    Это может произойти только если у Керол есть мандатные ссылки и на Алису и на Боба. Ссылка на Алису должна позволять передачу этого сообщения. Керол не может передать полномочия для доступа к Бобу в большем объеме чем у нее есть (может в меньшем, но об этом далее).
Эти правила позволяют представить рассматриваемую систему как граф объектов и формально анализировать существующие каналы связи, а также места и условия для появления новых связей.

С точки зрения модели, совокупность глобальных переменных (в любом виде, включая традиционную файловую систему) является единым объектом, который доступен всем. Существование такого объекта не противоречит модели, но является дырой в безопасности системы. Груз синглтонов и глобального доступа к файловой системе в стандатной библиотеке Java стал серьезной проблемой перед создателями системы J-Kernel. Те же проблемы присутствуют и в платформе .NET.

Как можно заметить, приведенные выше правила описывают как права доступа могут распространяться по системе, но не описывают как можно урезать ранее выданные права. И действительно, модель безопасности на основе мандатных ссылок исходит из предположения, что однажды выданные права, отобрать уже нельзя. Это свойство является причиной для критики модели, однако на самом деле не составляет проблемы (PDF).

Вместо того чтобы давать право «читать файл», программе дается право «читать файл, пока доступ не будет отозван». Реализуется это с помощью прокси-объектов. Нижеприведенный код иллюстрирует этот механизм в очень упрощенном виде:

using System;
using System.Collections.Generic;

namespace CapRevokeDemo
{
    // Интерфейс ресурса, доступ к которому мы контролируем
    interface IFile
    {
        char read(int index);
        int length { get; }
    }

    // Интерфейс субъекта, который пользуется ресурсом
    interface IApplication
    {
        void openFile(IFile reader);
    }

    class FileAlreadyExistsException : Exception
    {
    }

    class FileNotFoundException : Exception
    {
    }

    class AccessRevokedException : Exception
    {
    }

    // Компонент, которому пользователь доверяет управление своими ресурсами
    class FileManager
    {
        public FileManager()
        {
            files_ = new Dictionary<string, IFile>();
            openedFiles_ = new Dictionary<string,List<FileProxy>>();
        }

        public void addFile(string fileName, IFile reader)
        {
            if (files_.ContainsKey(fileName))
            {
                throw new FileAlreadyExistsException();
            }
            files_.Add(fileName, reader);
        }

        public void openFileInApplication(string fileName, IApplication app)
        {
            IFile reader;
            if(!files_.TryGetValue(fileName, out reader))
            {
                throw new FileNotFoundException();
            }

            FileProxy proxy = new FileProxy(reader);
            List<FileProxy> proxies;
            if(!openedFiles_.TryGetValue(fileName, out proxies))
            {
                proxies = new List<FileProxy>();
                openedFiles_.Add(fileName, proxies);
            }
            proxies.Add(proxy);
            app.openFile(proxy);
        }

        public void revokeAccessToFile(string fileName)
        {
            List<FileProxy> proxies;
            if(openedFiles_.TryGetValue(fileName, out proxies))
            {
                foreach(FileProxy proxy in proxies)
                {
                    proxy.revokeAccess();
                }
                openedFiles_.Remove(fileName);
            }
        }

        // Прокси-объект, с помощью которого реализуется отзыв доступа.
        private class FileProxy : IFile
        {
            public FileProxy(IFile file)
            {
                file_ = file;
            }

            public char read(int index)
            {
                if(file_ == null)
                {
                    throw new AccessRevokedException();
                }

                return file_.read(index);
            }

            public int length
            {
                get
                {
                    if(file_ == null)
                    {
                        throw new AccessRevokedException();
                    }

                    return file_.length;
                }
            }

            public void revokeAccess()
            {
                file_ = null;
            }

            private IFile file_;
        }

        private Dictionary<string, IFile> files_;
        private Dictionary<string, List<FileProxy> > openedFiles_;
    }

    // Тестовая программа
    class Program
    {
        class DemoFile : IFile
        {
            public DemoFile(string data)
            {
                data_ = data;
            }

            public char read(int index)
            {
                return data_[index];
            }

            public int length
            {
                get
                {
                    return data_.Length;
                }
            }

            private string data_;
        }

        class DemoApp : IApplication
        {
            public void openFile(IFile file)
            {
                file_ = file;
                index_ = 0;
            }

            public void closeFile()
            {
                file_ = null;
                index_ = 0;
            }

            public void read()
            {
                char c = file_.read(index_++);
                System.Console.WriteLine("c = {0}", c);
            }

            private IFile file_ = null;
            private int index_ = 0;
        }

        public static void Main(string[] args)
        {
            try
            {
                FileManager manager = new FileManager();
                manager.addFile("file", new DemoFile("SECRET"));

                DemoApp app = new DemoApp();
                manager.openFileInApplication("file", app);

                app.read();
                app.read();
                app.read();
                manager.revokeAccessToFile("file");
                app.read();
            }
            catch(Exception e)
            {
                System.Console.WriteLine("Exception: {0}", e.Message);
            }
        }
    }
}


Хочу обратить внимание, что из этого подхода следует, что о возможности отозвать доступ необходимо позаботиться заранее. Если для этого не было предпринято никаких мер, то сделать это постфактум невозможно!

Прокси-объекты могут использоваться не только для ограничения доступа по времени, но и по другим параметрам — можно предоставить доступ только к куску файла или, имея доступ на чтение-запись, создать прокси-объект, который разрешает доступ только на чтение. Подобным образом можно настраивать права доступа, специфичные для предметной области — можно разрешить оставлять комментарии в документе, но запретить менять содержимое самого документа. В традиционных системах эти операции разделить невозможно — на уровне ОС они обе выглядят как модификация бинарного файла.

Прокси-объекты могут применяться рекурсивно: один объект делегирует часть своих прав другому, второй — третьему и т.д. На каждом шаге передается только часть прав, а остальные отсекаются с помощью прокси-объектов. С каждой связью объем доступных привилегий становится все меньше и меньше. Это называется затуханием (англ. attenuation) прав доступа.

Защищенность от подделки


Мандатная ссылка обязана обладать важным свойством – защищенностью от подделки. Должно быть невозможным «угадать» мандатную ссылку, которая не была предоставлена в соответствии с вышеприведенными правилами.

Для дескрипторов это свойство неверно в рамках одного процесса: зная принцип формирования кодов дескрипторов можно за приемлемое время сформировать правильный дескриптор. Т.е. плагин, загруженный в процесс хоста, может получить доступ к данным, которые через интерфейс взаимодействия хоста с плагином никак не могли к нему попасть.

Для файловых путей это свойство не выполняется в принципе – достаточно написать в коде “C:\Windows\system32\drivers\etc\hosts” – и вот, у нас уже есть ссылка на важный системный файл.

Как же защитить мандатные ссылки от подделок? Для обеспечения этого есть мне известно четыре основных подхода:
  1. Мандатные ссылки как дескрипторы — Cубъектами являются процессы. Вся работа с мандатными ссылками осуществляется через функции ОС. Для каждого процесса ОС хранит список мандатных ссылок, которые доступны ему на данный момент. Процесс может выполнить операцию над мандатной ссылкой только, если эта ссылка присутствует в соответствующем списке.
  2. Криптографические средства – этот подход не дает абсолютной гарантии, что подделать ссылку будет невозможно, а вместо этого обеспечивает необходимую минимальную сложность подделки. Программа предоставляет зашифрованную мандатную ссылку функциям ОС, которые проверяют ее корректность и расшифровывают перед использованием. Из-за высоких накладных расходов на шифровку/расшифровку мандатных ссылок этот метод не используется для реализации безопасности в пределах одной машины, но используется в сетевых системах.
  3. Мандатные ссылки с паролями (англ. password-capabilities) – гибрид предыдущих двух методов (подробнее тут и тут). Мандатная ссылка состоит из идентификационной информации и пароля. Пароль представляет из себя случайную последовательность битов, сгенерированную системой. Вероятность угадать пароль составляет 2-k, где k – это длина пароля. Система хранит словарь, ассоциирующий пароль с идентификационной информацией. Мандатная ссылка считается корректной, если пароль в мандатной ссылке соответствует паролю в словаре.
  4. Защита на уровне системы команд/языка – система команд целевой машины вообще не содержит команд, позволяющих интерпретировать произвольную последовательность битов как мандатную ссылку, либо такие команды являются привилегированными и доступны только ядру. При этом целевая машина может быть как реальным устройством (Plessey System 250, IBM System/38, RSRE FLEX Computer System), так и виртуальной машиной (проект J-Kernel реализован на основе JVM)

Сериализация


Для практической применимости система, реализующая безопасность на основе мандатных ссылок, должна поддерживать сериализацию последних, а это весьма нетривиальная задача. Система должна гарантировать, что сериализированный образ мандатной ссылки не был подделан, что ресурс, на который указывает ссылка, по-прежнему существует, что процесс, выполняющий десериализацию, действительно является возрождением того процесса, который выполнил сериализацию.

Как отличное решение всех этих проблем зарекомендовал себя механизм прозрачной или ортогональной персистентности: операционная система берет всю заботу о сохранении и восстановлении состояния на себя, а приложения работают с абстрактной персистентной одноуровневой памятью. Время от времени операционная система сохраняет на диск полный снимок состояния всех процессов, а при загрузке – загружает в память последнюю удачно сохраненную версию. Программы при этом прямого доступа к диску не имеют и программно подделать сериализированный образ на диске не могут.

Заточение


Допустим, что Алиса хочет использовать программу, созданную Бобом, для обработки своих данных. Боб хочет чтобы никто другой не имел доступа к внутренностям его программы. Кроме того, Боб может захотеть параметризовать свою программу данными. Это повод предоставить Алисе программу не в виде массива байтов с исполняемым кодом, а в виде объекта. Но Алиса хочет быть уверенной, что эта программа не передаст ее конфиденциальные данные посторонним. А объекты могут хранить ссылки на другие объекты или иметь изменяемое состояние, которое могут прочитать другие объекты, т.e. использование объектов в данном случае представляет опасность для безопасности Алисы.

Наложение ограничений на объект, передаваемый Алисе, которые сделают этот объект безопасным для использования Алисой в данном сценарии, называется заточением объекта (англ. confinement).

Существует запатентованное решение, позволяющее добиться заточения в рамках описанной выше модели. Однако, как отмечают сами авторы этого решения, более элегантным и простым решением будет введение атрибута транзитивной константности, корректность которого должна обеспечивать система. Это гарантирует, что программе будет просто некуда записать конфиденциальные данные Алисы. В этом случае, если Алисе нужен объект с изменяемым состоянием, то переданный ей объект должен проектироваться как фабрика, создающая новые объекты из данных, содержащихся в самой фабрике, и данных, предоставленных Алисой.

Заключение


Модель безопасности на основе мандатных ссылок позволяет безопасно взаимодействовать взаимно подозрительным компонентам. Модель позволяет смело запускать на своей машине прозвольный код из неизвестного источника, не опасаясь за последствия — для традиционных систем это немыслимо. Моделью может пользоваться любой компонент системы, с любыми привилегиями, в то время как в традиционных системах для создания новых пользователей и групп требуются административные привилегии.

На мой взгляд, безопасность на основе мандатных ссылок наиболее удобно и элегантно реализуется вместе с ООП, безопасностью на основе языка и ортогональной персистентностью. Я верю, что платформа, реализующая все эти концепции, напишет следующую главу в эволюции ОС. Но пока что безопасность на основе мандатных ссылок остается уделом экспериментальных ОС. Надеюсь, мне удалось заинтересовать читателя идеей мандатных ссылок и тем самым немного приблизить тот день, когда подобный проект станет массовой ОС.

Ссылки:


  1. Marc Stiegler et al. Introduction To Capability Based Security
  2. J-Kernel
  3. Mark S. Miller et al. Capability Myths Demolished [PDF]
  4. Н. А. Олифер, В. Г. Олифер. Сетевые операционные системы. Глава 6. Микроядро Mach
  5. Tahoe-LAFS wiki
  6. Cunningham & Cunningham, Inc. wiki. Password Capability Model
  7. Ronald Pose. Password-capabilities: their evolution from the password-capability system into walnut and beyond
  8. Norm Hardy. The Confused Deputy (or why capabilities might have been invented)
  9. Mark S. Miller. The KeyKOS Factory

+13
4.7k 48
Comments 15