В-третьих, будьте осторожны с вводом и выводом, например с тем, где вы принимаете входные данные, и особенно если ваш код имеет дополнительные полномочия.
В-четвертых, не ослабляйте. Что, встрепенулись? Хорошо. Это правда, я говорил об ослаблении, но вместо того чтобы ослаблять, создайте именно тот набор полномочий, которые вы хотите передать каждому клиенту. (Например, если код используется в традиционной системе Windows или Unix, может быть разумно передавать файловые дескрипторы, а не имена файлов.)
В-пятых, доступ представителя должен быть либо точно таким же, как и на следующем уровне, либо явно отличаться. «Почти то же самое» может привести к недоразумениям. Неявно отличаться от других – часто значит «поразительно легко неправильно использовать».
Передача обслуживания
Обычная современная передача обслуживания осуществляется через протоколы приложений, такие как mailto. Эти протоколы кажутся простыми и безопасными, поскольку они односторонние, а данные, связанные с ними, ограничены тем, что может быть закодировано в URL-адресе. Но приложение, которому передаются данные, вполне может быть написано с предположением, что оно будет вызвано пользователем, и этот пользователь не будет атаковать себя, поскольку у него уже есть возможность выполнять код. Нарушение этого предположения часто приводит к забавным результатам, если предположить, что вы являетесь злоумышленником [Lawrence, 2019].
Mailto принимает в качестве аргументов адрес электронной почты, а также тему, копию, скрытую копию и основное содержимое. Я уверен, что моя почтовая программа готова к странному содержимому в поле скрытой копии, но я не уверен, что ваша готова. Говоря более прозаично, если вы реализуете такую систему передачи, важно понимать, что клиенты могут не ожидать случайного контента из интернета.
Средства защиты, которые вы встраиваете в код, и архитектурные шаблоны, которые вы развертываете, будут служить для сдерживания злоумышленников и, как мы надеемся, не позволят им достичь своих целей. На этом мы переходим к разговору о понятиях полномочий и привилегий.
Полномочия и привилегии
До сих пор я рассматривал полномочия и привилегии как взаимозаменяемые термины, предполагая, что вы, возможно, слышали о привилегиях, но не задумывались о том, что они означают. Если это так, вы можете передать это неточное понимание другим. Оказывается, что концепция привилегий в компьютерной безопасности – это беспорядок, который делает вашу работу намного сложнее, чем она должна быть. Например, в Unix-системах «привязка к порту с низким номером» была привилегией, зарезервированной для root; в Windows это было не так. (Это даже не было зарезервировано для группы «Администраторы».)
Если вы вынуждены работать в системах привилегий и разрешений, понимание того, почему с ними что-то может пойти не так, может помочь вам подумать о создании надежной защиты. Если вы можете их заменить, это будет похоже на переход от языка с неявным приведением типов к языку со строгой типизацией. Поначалу это немного сложнее, но целые классы ошибок могут исчезнуть, и вы сможете писать код быстрее и увереннее.
Основной задачей операционной системы является управление доступом к ресурсам, включая процессоры, хранилище и периферийные устройства. Она определяет учетные записи и использует их для определения и проверки авторизации с помощью различных системных вызовов. Операционные системы также определяют, как часто и в каком объеме эти ресурсы (особенно процессор) могут быть доступны через квоты.
Реализация управления доступом
Эти вызовы часто включают кортеж типа (userid, action, object).
Вот пример: (adam, read, /home/adam/.bashrc).
Если более явно, то код, реализующий управление доступом, обычно выглядит примерно так:
fd = open(uid, file, flags) {
// flags – это read, write, execute, и т. д.
if (uid == root) return (kernel_open(file, flags))
if (check_permissions (uid, file, flags) return
(kernel_open(file, flags)
// uid следует рассматривать как отдельный слой,
// но здесь он используется для наглядности
// Также такая конструкция означает, что любой
// вызывающий может открыть файл, как и любой UID,
// чего, вероятно, проектировщик не хотел
uid – это субъект, самая мелкая единица, которой могут быть предоставлены права доступа. Файл является объектом, мельчайшей единицей, для которой могут быть определены права доступа.
Флаги могут быть простыми, такими как read, write, execute, create или delete, или более тонкими, такими как append. Этот шаблон существует во многих системах, включая настольные или мобильные операционные системы, а также облачные сервисы, такие как Dropbox. Шаблон отображается в базах данных или других приложениях, когда file заменяется каким-либо другим дескриптором, например строкой, столбцом или хранимой процедурой.
Эти кортежи часто выражаются в виде разрешений или управления доступом. Примером разрешения может быть (adam, execute, a.out). Существуют пределы того, насколько выразительным можно быть в Unix-модели разрешений для пользователя, группы и всех остальных, поэтому некоторые системы определяют списки управления доступом (ACL), которые представляют собой списки инструкций управления доступом и правил о том, как обрабатывать конфликты, такие как «любое запрещающее правило имеет приоритет» или «побеждает наиболее конкретное правило». Если быть точным, то это записи управления доступом (ACE), хранящиеся в списках ACL. На практике ACL иногда используется для обозначения записи, списка записей или звука, который издает сбитый с толку инженер, пытаясь понять все это.
Расширение полномочий может позволить злоумышленнику обойти эти типы проверок или сделать что-то, выходящее за рамки намерений ответственного за это человека.
Разрешения и политики в управлении доступом
Такие выражения, как (adam, ~/.bashrc, read), (group: staff, a.out, execute) или (adam@example.org, flickr.com/photos, modify) являются заявлениями политики. Они представляют собой структурированные выражения намерений владельца системы или пользователей относительно того, кто и что может делать.
В идеале они должны быть понятны и соответствовать намерениям пользователя, который устанавливает политику, но и то и другое оказывается сложным. Пользователи могут не знать, кто входит в группу; каскады наследования ACL могут работать не так, как ожидалось. Данные могут быть доступны неожиданным сторонам, и может быть трудно предоставить доступ именно нужной группе. Возможно, вы захотите что-то разрешить «системным разработчикам, которые знают местоположение второй „Звезды смерти“», но исключить членов групп «ботаны» и «охотники за головами».
Кортежи (пользователь, объект, действие) имеют несколько серьезных недостатков, в том числе разрешение имен, но, что более важно, мы даем каждой программе очень широкие полномочия действовать с объектами от нашего имени, потому что предсказать полномочия, которые понадобятся этой программе, очень сложно.
Прогнозы сложны, особенно в отношении политик
Если мне нужно будет высказать свою политику, направленную против будущих потребностей, я, скорее всего, сделаю это широко, потому что я не знаю, что будет в будущем.
Но в данный момент я, возможно, смогу описать очень конкретную политику. Например, расширенная политика может выглядеть следующим образом: «Microsoft Word может читать и писать в ~/Documents и вложенные папки». Это то, чего я обычно хочу, пока Word не будет взломан и злоумышленник не использует эти делегированные полномочия для вымогательства после захвата всех моих файлов. Сегодня я хочу, чтобы Word писал только в ~/Documents/threatsbook/expansion.docx.
Если вы собираетесь внедрить песочницу, одна из проблем заключается в том, как заставить человека сказать: «Эта программа может получить доступ к этому файлу, но ни к какому другому файлу, к которому у меня есть доступ», – и сделать это так, чтобы это было легко, не раздражало и защищало от будущих угроз.
Когда была представлена Windows 8, она лишила приложения в песочнице возможности вызывать традиционную функцию open(). Вместо этого приложения вызывают FileOpenPicker и FileSavePicker. Как показано на рисунке 6.4, эти API и связанные с ними пользовательские интерфейсы (средства выбора файлов) работают на более высоком уровне полномочий, чем приложение, и человек за компьютером использует их, чтобы указать, где приложение может читать или писать. Большинство людей даже не подозревают, что существует разница в безопасности между диалоговым окном выбора файлов и приложением. В качестве забавного примечания: я узнал об этом из разговоров с людьми, которые его разрабатывали, и было сложно найти документацию, подтверждающую эти утверждения. В документации Microsoft «Открытие файлов и папок с помощью средства выбора» не упоминается безопасность или способности. Средство выбора файлов не указано в качестве границы на странице «Границы безопасности» (которая, справедливости ради, является COM-страницей, высоко ранжированной поисковыми системами по своему названию), но доступ к Documents указан как ограниченная способность в «Объявлениях способностей приложений» [Microsoft, 2018a, c и d].
Проблемы с привилегиями и разрешениями
С разрешениями связана идея привилегий. Давайте на мгновение определим привилегии как способность изменять функции, свойства или правила безопасности в системе. Я склонен думать о разрешениях как привязанных к объектам, а о привилегиях – как привязанных к учетным записям, но мы быстро достигаем пределов такого различения.
Рис. 6.4. Архитектура средств выбора файлов
Привилегии могут быть неявными или явными. В Windows или Mac даже «обычная» учетная запись имеет невероятно редкую и неявную привилегию: запускать код на этой машине. Большинство людей в мире не имеют на это полномочий. «Выполнение кода» – это привилегия, часто неявно предоставляемая любому авторизованному пользователю традиционной операционной системы, но на мобильных телефонах она ограничена через магазины приложений, а в терминалах самообслуживания в ней отказано. Точно так же send network packets является привилегией, как и listen for packets и listed on ports below 1024 – отдельной привилегией. Как вы видели, Linux теперь пытается разбить эти привилегии на то, что он назыв