Защита систем. Чему «Звездные войны» учат инженера ПО — страница 41 из 68

ает способностями.

Мобильные приложения должны соглашаться с тем, что сетевые привилегии могут быть произвольно удалены, если возникает проблема с пропускной способностью сотовой связи. Их также можно удалить в режиме полета, но это отключение приемопередатчика, отдельная привилегия. Есть тонкая и важная разница. Приложение, повышающее привилегии, может отправлять свои данные через другое приложение, когда привилегия первого приложения «отправлять пакеты» удалена, или полностью отключать режим полета – привилегия, которая не должна быть доступна ни одному обычному приложению. (То же самое можно сказать и о понятии полномочий: приложение, имеющее право отправлять или принимать данные с помощью приемопередатчика, может не иметь полномочий на его включение или выключение.) Кстати, это характеристика надежности сотовой сети, а не самолета.

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

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

Разрешения полезны отчасти потому, что они знакомы. Мы научились их понимать. Разработка разрешений для предоставления дополнительных способностей, которые не эквивалентны администратору, является сложной задачей. Например, оператор резервного копирования Windows всегда был эквивалентен администратору, а различные привилегии Windows были потихоньку выведены из оборота. Дроиды получали доступ ко всем объектам через порты доступа, пока некая единица R2 не разрушила его для всех.

На самом деле, проектировщики систем могут определять как разрешения, так и привилегии и использовать их для достижения тех же результатов. Например, в Unix учетная запись создается путем изменения file:///etc/passwd. Таким образом, разрешение на запись в file:///etc/passwd предоставляет возможность создавать учетные записи. Это разрешение на запись может быть предоставлено путем вызова chmod для файла, путем добавления пользователя в группу wheel, с помощью sudo или, возможно, другими способами. В Windows учетная запись создается путем вызова NetUserAdd() или других API, каждый из которых проверяет вызывающую сторону на членство в группе администраторов или операторов учетных записей. Таким образом, в Unix создание учетной записи является вопросом разрешений, в то время как в Windows создание учетной записи является привилегией.

Если сделать шаг назад, то попытка понять привилегии с помощью определения объекта / учетной записи даже не сработает. В Windows пользователи – это объекты, объекты – черепахи, и безумие ждет тебя на этом пути. Дело даже не в темной стороне Силы – мой гнев не делает меня сильнее. Нам нужно пройти по этой тропе еще немного, чтобы вы могли понять, почему нам нужны альтернативные пути достижения прогресса.

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

Большинство современных систем настольных компьютеров используют разрешения, а не полномочия, и это наносит ущерб им и нам. Системы, которые делегируют через полномочия и реализуют принцип наименьших полномочий, могут быть гораздо более безопасными. Теперь я называю букву E в STRIDE «расширением полномочий» (expansion of authority).

Новейшие подходы к политикам

Опять же, полномочия означают эффекты, которые программа может вызвать в объектах, к которым она может получить доступ, либо непосредственно по разрешению, либо косвенно через разрешенные взаимодействия с другими программами. Это позволяет нам рассуждать на уровне политик и разрабатывать механизмы для поддержки этих политик. (Вы можете подумать, что мы занимаемся эзотерикой, но это важно для того, чтобы придать вашим проектам прочную основу.) Но если вы разрабатываете современную технологию для чего-то вроде устройства интернета вещей или микросервисной облачной архитектуры, полезно задаться вопросом, какие полномочия должен иметь каждый процесс. Ответ на этот вопрос позволяет ответить и на вопрос «Какие разрешения должны быть установлены?», а также побуждает вас задуматься о том, где и как мы устанавливаем политику для системы.

Полномочия как проектный шаблон

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

Простой пример, цитируя д-ра Марка Миллера, показывает разницу между ними:

$ cp foo.txt bar.txt

Ваша оболочка передает в программу cp две строки: foo.txt и bar.txt. Программа cp использует эти строки, чтобы определить, какие файлы она должна скопировать.

Для сравнения рассмотрим, как cat выполняет свою задачу:

$ cat < foo.txt > bar.txt

Ваша оболочка использует эти строки, чтобы определить, какие файлы вы хотите обозначить. Как только эти имена будут разрешены, ваша оболочка передаст cat прямой доступ к файлам в виде открытых файловых дескрипторов. Программа cat использует эти дескрипторы для выполнения копирования. Теперь подумайте о наименьшем полномочии, которое необходимо каждому для выполнения своей задачи. В случае cp вы сообщаете программе, какие файлы копировать, передавая ей строки. Под этими строками вы имеете в виду определенные файлы в вашей файловой системе, которые должны быть разрешены в вашем пространстве имен файлов. Для того чтобы cp могла открывать файлы, которые вы называете, у нее уже должны быть права на использование вашего пространства имен, а также права на чтение и запись любого файла, который вы можете назвать. При таком способе использования имен наименьшие права cp по-прежнему включают в себя все ваши права доступа к файловой системе. Наименьшие полномочия, в которых она нуждается, настолько широки, что делают достижение безопасности или надежности невозможным.

В случае cat вы сообщаете ей, какие файлы копировать, передавая желаемый доступ (на чтение или запись) к этим двум конкретным файлам. Как и в примере cp, вы по-прежнему используете имена из вашего пространства имен, чтобы указать, какие файлы вы хотите скопировать, но эти имена оцениваются в вашем пространстве имен перед передачей в cat. Передавая cat дескрипторы файлов, а не строки для преобразования в дескрипторы, мы уменьшаем полномочия, необходимые для выполнения своей работы. Наименьшие полномочия для нее – это то, что вы ожидаете: право на доступ к foo.txt и право записи в него bar.txt. Другой доступ к вашей файловой системе для нее не требуется.

Другими словами, если cp запущена от имени anakin, ее оболочка дает cp полномочия на чтение любого файла, который anakin может прочитать; ей передаются все его полномочия, и мы знаем, что из этого выходит. В отличие от этого, cat имеет право читать ровно один файл и писать в другой. В политике, изложенной в предыдущем разделе, отсутствуют идентификация программ. Любая программа, запущенная от имени uid adam, может читать. bashrc. Для многих это кажется естественным положением дел. В системе, ориентированной на полномочия, мы должны уметь рассуждать о наборе прав доступа, который имеет программа.

Изменить систему, поменяв подход, основанный на разрешениях и привилегиях, на подход, основанный на полномочиях или способностях, – это тяжелая работа. Но она того стоит, потому что лишь немногие программы, запущенные от имени пользователя Джорджа – george, имеют все полномочия его учетной записи. У каждого есть те, и только те нужные ему полномочия, чтобы выполнять работу, которую он выполняет в данный момент.

Например, emacs очень мощный. Он способен выполнять Lisp-код, который может делать все, на что имеет право пользователь, который его вызывает. Предположим, что мы модифицируем его таким образом, что все вызовы read() заменяются пользовательским интерфейсом выбора файлов, подобным тому, который обсуждался в разделе «Прогнозы сложны, особенно в отношении политик». (Мы не будем обращать внимания на некоторые сложности, такие как чтение файлов, исполняемых при загрузке системы.)

Теперь наш модифицированный emacs не сможет прочитать сценарий «Мести джедая», если только Ларри, Лея или Джордж не кликнут на него. Он не сможет запустить программу-вымогатель, которая зашифрует сценарий «Скрытой угрозы». (Ладно, может быть, это было бы неплохо, но что бы вы ни думали об этой идее, мы не хотим предоставлять такие полномочия каждой программе, которую запускаем.)

Оказывается, это может быть легко, если вы контролируете операционную систему. Вы «просто» делаете fopen() вызовом, для использования которого требуется специальное разрешение, и добавляете API выбора файлов, который требует участия человека. (Опять же, есть подводные камни. Если вы измените fopen(), то все, что его вызывает, должно быть повторно проверено.)