Архитектура защиты
Разграничение доступа должно отвечать на вопрос, что кому позволено, но фактический ответ дает реализация выбранного механизма – например, монитора обращений, который сверяет запрос с политикой и разрешает либо отказывает. Реализация воплощается в программном обеспечении, которое содержит ошибки, и ни одно требование к политике не способно ответить, что станет с ней, если ошибка окажется в самом мониторе. Любые дополнительные механизмы сталкиваются с той же проблемой.
Решение состоит в том, чтобы дополнить меры против конкретных угроз, названных в модели угроз, общими системными требованиями, которые называют требованиями к конструкции. Они ограничивают последствия ошибки или непредусмотренной угрозы.
11.1. Доверенная вычислительная база
Не всякая ошибка одинаково опасна для защиты. Ошибка в текстовом редакторе портит документ: редактор исправляют, документ восстанавливают из копии, и защищенность системы от этого не меняется. Ошибка в проверке прав открывает все документы сразу, и подстраховать ее нечем, поскольку остальные части системы работают в предположении, что проверка выполнена верно. Различие в том, восстанавливается ли защита правкой одного места или требует пересмотра всей конструкции.
Совокупность компонентов системы (аппаратуры, микропрограмм и программ), от правильной работы которых зависит выполнение политики безопасности, называют доверенной вычислительной базой (TCB, Trusted Computing Base). Принадлежность к базе определяется последствием отказа компонента независимо от его назначения (например, драйвер файловой системы попадет туда, поскольку ошибка в нем позволяет читать чужие данные, равно как и любая программа, работающая с правами администратора).
Требование быть максимально компактным, чтобы гарантировать возможность полной проверки (которое ставилось ранее к монитору обращений) распространяется, таким образом, на всю базу целиком. Оно называется экономией механизма.
Исходный текст ядра Linux в январе 2025 года превысил 40 миллионов строк. В работающую систему попадает меньшая часть, однако все, что попадает, включая драйверы устройств, исполняется в пространстве ядра с полными правами и потому входит в доверенную вычислительную базу. Объем такого порядка не проверяется ни прочтением, ни доказательством. Микроядро seL4 насчитывает 8 700 строк на языке C и около 600 строк на ассемблере. В 2009 году для него было завершено машинно-проверенное доказательство того, что код соответствует формальной спецификации. Доказательство заняло порядка 200 тысяч строк на языке системы Isabelle и около 20 человеко-лет, из которых 11 пришлись собственно на seL4, а остальное на инструменты и библиотеки. Само ядро было спроектировано и написано за 30 человеко-месяцев. Доказательство обошлось почти на порядок дороже того, что доказывалось. При этом даже оно не означает «ошибок нет». Утверждение о корректности опирается на допущения, перечисленные авторами явно: верен не покрытый доказательством ассемблерный код; верна аппаратура; ни одно устройство не обращается к памяти напрямую в обход процессора; верны примерно 1 200 строк кода начальной загрузки. Допущение о правильности компилятора впоследствии снято отдельным доказательством соответствия двоичного кода исходному, прочие остались. Утверждение «ядро верно» имеет смысл только вместе с этим перечнем, равно как утверждение о стойкости имеет смысл только вместе с указанием противника.
Чтобы сократить базу, не отказываясь от нужных возможностей, используются следующие приемы:
- Вынести компонент за пределы базы. В микроядерной архитектуре драйверы исполняются как обычные программы с ограниченными правами. Ошибка в драйвере роняет только драйвер.
- Подвести под базу слой меньшего размера. Гипервизор проще операционной системы, и гостевые системы в базу друг для друга не входят.
- Вынести секрет туда, куда остальной код не дотягивается. Аппаратный криптографический модуль, доверенный платформенный модуль, анклав процессора выполняют операции с ключом, но выдать сам ключ не умеют. База сжимается до устройства, чья ограниченность и составляет защитное свойство. Так же устроено хранение биометрического шаблона внутри устройства.
При этом независимо от желания разработчиков и специалистов по безопасности действует обратная тенденция, часто незаметная: база постоянно растет, в том числе за счет программ, добавленных ради защиты.
В июне 2016 года подразделение Project Zero обнаружило, что в продуктах Symantec и Norton распаковщик исполняемых файлов работает в пространстве ядра. Перехватчик проверял все файловые операции, поэтому для выполнения произвольного кода с правами ядра достаточно было прислать письмо; открывать вложение не требовалось. Уязвимость затронула 25 продуктов.
В июле 2024 года обновление конфигурационного файла для драйвера CrowdStrike Falcon вывело из строя около 8,5 миллиона машин под управлением Windows. Противника в этой истории не было вовсе, ошиблись разработчики самого средства защиты, но исполнялся их код в ядре, то есть в базе.
В первом случае пострадала конфиденциальность, во втором доступность, а проблема одна. Программа, добавленная в базу ради защиты, окупается лишь тогда, когда предотвращенный ею ущерб превышает ущерб от ее собственных ошибок, а вероятность этих ошибок в расчет обычно не входит.
Таким образом, практический вопрос к любой системе состоит в том, чтобы определить ее доверенную базу. Перечень редко оказывается коротким и почти никогда не оказывается полным с первого раза, а все, что в него попало, придется либо проверять, либо принимать на веру. Плата за сокращение базы измеряется отброшенными возможностями, деньгами и трудоемкостью и ограничена сверху тем же правилом, что и всякая защита: ее стоимость не должна превышать ценность защищаемого.
11.2. Разделяемый механизм и изоляция
Разграничение доступа описывает обращения к именованным объектам, но два субъекта, полностью разведенные по правам, все равно пользуются общими механизмами: процессором, оперативной памятью, дисковым кэшем, библиотекой, сервером базы данных. Всякий разделяемый механизм служит каналом связи: то, что один субъект с ним делает, второй способен наблюдать, не нарушив ни одного правила доступа.
К примеру, если при проверке пароля сравнение осуществляется обычной строковой операцией – посимвольно, прекращаясь на первом несовпадении, время ответа зависит от числа угаданных символов. В этом случае противник будет подбирать пароль по одному символу за раз вместо перебора всех сочетаний, извлекая сведения из наблюдаемых временных или физических характеристик обработки сообщения. Такие каналы называют побочными (кроме времени ими служат потребляемая мощность, электромагнитное излучение, звук и т.п.).
Кэш процессора разделяют все программы, исполняемые на одном ядре. Обращение к данным, вытесненным из кэша чужой программой, выполняется заметно дольше, поэтому программа, измеряя время собственных обращений, восстанавливает, к каким областям памяти обращался сосед. На этом основано извлечение ключа из реализаций AES, работающих через таблицы подстановки (последовательность обращений к таблице зависит от ключа). Правила доступа при этом не нарушаются.
Та же конструкция была обнаружена в 2018 году на уровне самого процессора. Для увеличения производительности процессор способен выполнять команды с опережением (например, предсказывать ветвь условного оператора, не дожидаясь, пока условие перехода будет вычислено, а им может оказаться и проверка прав доступа). Не подтвердившийся результат отменяется, но следы обращений остаются в кэше и вычитываются описанным способом. Прочитать так можно память ядра и чужих процессов. Существенно, что уязвимой оказывается правильно написанная программа в правильно настроенной операционной системе (дефект находится в разделяемом механизме, о существовании которого прикладной программист не осведомлен). Исправление потребовало переделки ядер операционных систем и микрокода и сказалось на быстродействии, а для части атак этого семейства единственной надежной мерой оказалось отключение одновременной многопоточности, то есть отказ от половины логических процессоров (так поступила OpenBSD).
Требование сокращать объем механизмов, разделяемых между субъектами, называют минимумом общего механизма.
Изоляция строится ступенями. Каждая следующая ступень сокращает общий механизм. Два процесса в одной системе делят ядро, кэш, планировщик и файловую систему. Два контейнера делят ядро, то есть общий механизм остается огромным, и ошибка в ядре снимает границу между ними. Две виртуальные машины делят гипервизор, который меньше ядра. Две физические машины делят только сеть и электропитание.
Стоимость растет в том же порядке: разделяемая машина в облаке дешевле выделенной в несколько раз. Кэш, планировщик, общая библиотека, общий сервер существуют ради экономии, и полный отказ от общего механизма означает отказ от нее. Поэтому принцип соблюдается частично и осознанно, а проектный вопрос ставится так: между какими субъектами не должно остаться ни одного общего механизма и во что обойдется их развести.
11.3. Посредник и кэширование решения
Проверять права при каждом обращении дорого. Одно чтение файла складывается из тысяч системных вызовов, одна веб-страница собирает десятки запросов к серверу, и каждый раз посреднику приходится заново выяснять, кто обратился и что ему позволено. Поэтому решение принимают один раз и запоминают. С этого момента система живет уже не по политике, а по своей памяти о ней.
Права сверяются, когда файл открывают, и система выдает дескриптор. Дальше чтение и запись идут по номеру дескриптора и никаких проверок не вызывают, так что администратор, отобравший доступ, не закроет этим файл, открытый минуту назад. Windows вычисляет права при входе и складывает их в маркер доступа, поэтому сотрудник, которого только что включили в новую группу, ничего не получит, пока не выйдет и не войдет снова. Билет Kerberos доживает свой срок до конца, что бы за это время ни случилось с учетной записью.
Требование проверять полномочия при каждом обращении, без исключений и обходных путей, называют полным посредничеством.
На практике это требование редко выполняется полностью из-за неэффективности. Плата взимается на каждом обращении, а выигрыш получается только в тех нечастых случаях, когда права успели измениться. Между политикой безопасности и поведением системы всегда остается зазор, ширина которого – время жизни запомненного решения. Задача проектировщика – найти компромисс. Зазор в секунду обойдется в проверку почти при каждом запросе. Зазор в час – это лишний час времени в системе для уволенного сотрудника. В схеме с полномочием, которое обсуждалось при сравнении способов хранения матрицы доступа, проблема доходит до крайности. Субъект получает на руки полномочие, которое можно предъявлять, не беспокоя владельца объекта. Это очень удобно, но посредника в схеме нет, и немедленный отзыв выданного полномочия невозможен.
Отдельно необходимо спроектировать, как поступать посреднику, когда возможности принять решение нет (правило не найдено, справочник атрибутов не отвечает, сервер политики упал). Политика безопасных умолчаний заключается в том, чтобы в такой ситуации принимать решение об отказе (за исключением особых случаев, для которых заранее должно быть предусмотрено поведение системы).
При проектировании самых различных механизмов информационной безопасности логика компромисса всегда базируется на сравнении двух категорий ошибочных решений.
Ложный допуск: неправильный объект принят за правильный. Например, зараженный файл помечен как безопасный, спам признан обычным письмом, субъект без прав получает доступ.
Ложный отказ: правильный объект принят за неправильный. Безопасный файл распознан как зараженный, легальное письмо улетело в спам, в доступе отказано надлежащему субъекту.
В зависимости от конкретной системы сравнительная цена таких ошибок будет различной. Для антивируса лучше лишний раз заблокировать чистый файл, чем однажды пропустить зараженный, в то время как для спам-фильтра потерянное письмо намного хуже, чем несколько дошедших рассылок. Турникет при пожаре разблокируется. Медицинскую карту в приемном покое откроют врачу, которого нет в списке лечащих. Безопасным умолчанием здесь оказывается допуск. Если запертая дверь обходится дороже лишнего посетителя, защита переносится с предотвращения на обнаружение. Доступ дают, но записывают, и эта запись останется единственным способом поправить неверное решение.
11.4. Дробление полномочий
Запущенная программа работает с правами того, кто ее запустил, и получает их целиком. Текстовому редактору нужен один файл, а доступны ему все файлы владельца. Почтовому клиенту нужны сеть и папка с письмами, а стереть он может что угодно. Пока программа работает правильно, разницы не видно. Разница появляется, когда в ней находят ошибку, потому что захвативший программу противник получает не то, что ей было нужно, а все, что ей было доступно.
Поэтому права стараются нарезать мельче и выдавать ненадолго. Лучше всего это видно на телефоне. Приложению для заметок незачем знать, где находится владелец, а фонарику незачем читать список контактов, поэтому каждое разрешение приложение просит отдельно, а владелец соглашается или отказывает. Со временем к объему прав добавился срок, и разрешение выдают только на то время, пока приложение открыто, а иногда и на один-единственный раз, потому что приложение, получившее микрофон навсегда, слушает и тогда, когда его никто не запускал. Тот же прием работает в настольной системе. Повседневные дела не требуют прав администратора, поэтому под администратором не работают постоянно, а берут повышение на одно действие, и система спрашивает подтверждение всякий раз, когда программа собирается изменить что-то за пределами файлов самого пользователя. Тот, кто сидит администратором всегда, отдает противнику машину целиком вместе с первой же запущенной по ошибке программой. Наконец, тот же расчет стоит за кнопкой «Войти через...». Прежде чем связать свою учетную запись с чужим приложением, владелец видит перечень того, что приложение получит, и право читать список файлов не означает права их удалить.
Требование выдавать субъекту ровно те права, которые нужны для работы, и ровно на то время, пока они нужны, называют минимумом привилегий. Это достоинство отмечалось у ролевой модели, где сотрудник получает права должности, а не права системы.
Дробление оплачивается администрированием. Каждое отдельное право нужно кому-то выдать, где-то записать, пересмотреть при переводе сотрудника и отобрать при увольнении. Права, выданные однажды «на всякий случай», обычно не отбирают, и сотрудник, поменявший три отдела, носит с собой права всех трех. Регулярный пересмотр стоит рабочего времени и потому делается редко.
Чем мельче нарезка, тем быстрее перечень расходится с действительностью, и с какого-то момента дробление начинает работать против себя. Подробный список, который два года никто не открывал, вреднее короткого и верного. Короткий сразу выглядит грубым, и ему не доверяют. Длинный выглядит так, будто права разложены по местам, поэтому проверять его не идут. Дробить имеет смысл ровно настолько, насколько «измельченный» перечень практически возможно поддерживать.
Второе требование касается не объема полномочия, а числа людей, которых для него достаточно. Крупный платеж подтверждают двое. Код в промышленную среду выкладывает не тот, кто его написал. Ключ подписи собирают из долей, и ни одна доля в отдельности ничего не открывает (разделение секрета). Такое требование называют разделением полномочий. Оно увеличивает стоимость взлома: сговор обходится дороже, готовится дольше и оставляет больше следов. При этом стойкость растет за счет падения доступности. Двое подтверждающих означают, что работа встанет, когда один болеет, а второй в отпуске.
Для решения проблемы можно завести аварийный доступ, но в этом случае именно он и станет первой целью противника. Чем строже основное правило, тем чаще пользуются исключением, и в какой-то момент исключение превращается в обычный порядок работы. Поэтому аварийный доступ делают дорогим в использовании. Каждое обращение к нему записывают отдельной строкой, разбирают на следующий рабочий день и требуют объяснений. Так же поступают с правами, которые не удалось раздробить, и с обязанностями, которые нельзя распределить. Все, что не удалось предотвратить, оплачивается записью, и решение о записи принимают тогда же, когда и решение о послаблении.
Оба требования существуют одновременно на трех уровнях. Технически права нарезают средствами системы. Организационно назначают, кто чьи действия подтверждает и какие обязанности нельзя совмещать в одном лице. Правовой уровень закрепляет ответственность, без него разделение держится на одной добросовестности.
Минимум привилегий не уменьшает числа ошибок и не мешает противнику захватить программу, но уменьшает цену захвата. Разделение полномочий снижает вероятность нарушения, потому что требует от противника иметь сообщника, но платит за это доступностью.
Требования к конструкции не зависят от того, откуда придет нападение. Они действуют одинаково против любой угрозы, потому что ограничивают последствия ошибки, где бы она ни оказалась. Поскольку каждое из этих требований имеет свою цену, предел, до которого стоит их доводить, определяется с помощью модели угроз.
