Безопасность в Silverlight 2

ОГЛАВЛЕНИЕ

Для дальнейшего обсуждения тонкостей модели прозрачности будут полезны сведения об устройстве и функционировании системы безопасности CLR в рамках Microsoft .NET Framework. В CLR реализован механизм определения прав и возможностей кода, которые представлены разрешениями. Этот механизм основывается на информации о коде: откуда получен исполняемый код, подписан ли код цифровой подписью и, если да, то кому принадлежит эта подпись, и так далее. В результате коду, полученному из ненадежных источников, таких как Интернет, предоставляются меньшие привилегии, чем коду, полученному из надежных источников, например из папки «Program Files».

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

Чтобы облегчить реализацию и использование этой разделенной модели, в .NET Framework 2.0 был представлен так называемый «простой API песочницы». С помощью этого API для каждой области приложения можно задать «песочницу» со своим набором прав, а также задать список полностью проверенных сборок, не входящих в глобальный кэш сборок (GAC). Сборки, входящие в глобальный кэш сборок, уже считаются полностью проверенными. Все приложения, которые загружаются в такую область приложений и не входят в список полностью проверенных сборок, получают набор прав «песочницы». Если приложение запущено из Интернета, ему будет присвоен встроенный набор разрешений для Интернета.

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



Модель прозрачности

Модель прозрачности была известна со времен выхода .NET Framework 2.0, откуда она и была перенесена в CoreCLR (так называется версия CLR для Silverlight). Она была разработана, чтобы решить проблему все увеличивающегося количества библиотек инфраструктуры, которые позволяли обращаться к службам из частично проверенного кода, что вызывало проблемы с внутренним аудитом безопасности. Использование библиотек с установленным атрибутом AllowPartiallyTrustedCallers (APTCA) позволяло обращаться к полностью проверенным службам из частично проверенного кода. Из-за этой потенциально опасной операции над определенной частью любой библиотеки с атрибутом APTCA приходилось проводить необходимые проверки безопасности.

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

Основной целью разработки модели прозрачности было создание четких границ между областями исполнения привилегированного и непривилегированного кода. В данной модели использовались две категории кода — Transparent («прозрачный») и Critical («ключевой»). На код, относящийся к категории «прозрачного», накладывались следующие ограничения:

  • В нем не должны были подтверждаться полномочия и повышаться права.
  • В нем не должно было содержаться небезопасного или непроверяемого кода. В противном случае операция, встретившаяся в прозрачном коде, рассматривалась как ключевая и в прозрачный код включались необходимые требования.
  • Ключевой код нельзя было вызывать напрямую. Исключения из этого правила объясняются ниже.
  • Нельзя было вызывать системные функции или API, помеченные атрибутом SuppressUnmanagedCodeSecurity.
  • Нельзя было удовлетворять LinkDemands, все эти требования должны были приводить к полному анализу стека.

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

Как же происходит взаимодействия кода, принадлежащего к разным категориям? Если разработчик уверен, что вызов некоторый ключевого API можно безопасно выполнить из прозрачного кода (например, обращение к API операционной системы, который возвращает дату и время), он помечает такой вызов как TreatAsSafe (в коде используется атрибут SecurityTreatAsSafe). Важно отметить, что в рамках модели прозрачности версии 2.0 правила прозрачности применяются только внутри сборки, в результате все общедоступные ключевые модули неявно становятся TreatAsSafe. (В этой статье я буду ссылаться на эту модель прозрачности как на модель прозрачности версии 2.0 . Такая модель прозрачности используется в .NET Framework версий 3.0, 3.5 и 3.5 с пакетом обновлений 1). В модели прозрачности версии 2.0 общедоступный ключевой метод может быть вызван из прозрачного кода.

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



Прозрачность в CoreCLR

Несмотря на то, что механизм прозрачности помогает оценить степень безопасности сборки, он не является единственным средством обеспечения безопасности в .NET Framework. У разных областей приложений могут быть разные «песочницы» с разными наборами прав — загруженная из Интернета программа не должна иметь такие же полномочия, как и приложение из корпоративной интрасети. Таким образом, в коде необходимо проверять и подтверждать разрешения.

В случае с CoreCLR можно сделать несколько упрощений, исходя из того факта, что все приложения с расширенными возможностями, построенные с помощью CoreCLR, запускаются в веб-обозревателе. Первое упрощение заключается в том, что у каждого приложения есть установленная «песочница» и точно известно, какие права будут у каждого приложения Silverlight. Отпадает необходимость иметь дело с индивидуальными разрешениями. Есть только два набора прав: для полностью проверенных приложений и для «песочницы» Silverlight. Второе упрощение заключается в том, что все приложения Silverlight считаются частично проверенными, следовательно, не могут установить себе большие полномочия, чем полномочия «песочницы» Silverlight.

Из этих упрощений следует, что: все приложения Silverlight прозрачны и им присваивается набор прав «песочницы» Silverlight, библиотеки платформы Silverlight (большая часть которых полностью проверена) будут вызываться только приложениями Silverlight, значит, запросов особенных разрешений не будет; достаточно пометить опасные API как ключевые. Наконец, поскольку отсутствуют запросы, то останавливать анализ стека разрешений никогда не потребуется, значит, подтверждения тоже не нужны.

Таким образом, единственным механизмом обеспечения безопасности в CoreCLR является прозрачность, а большую часть CAS можно проигнорировать. Чтобы получить более надежный механизм обеспечения безопасности, правила для прозрачного кода были ужесточены, а путем дополнительных улучшений система прозрачности стала более понятной и эффективной.

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

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

В Silverlight весь код прозрачен по умолчанию. В том числе и библиотеки платформы Silverlight; весь ключевой код нужно явно пометить. Что предоставляет безопасное поведение по умолчанию, которое уменьшает шансы создания неявного ключевого метода по ошибке. В связи с наследованием типов и переопределением методов добавились и другие правила, о которых я напишу в разделе «Правила наследования».

Раньше я отмечал, что ключевой API, помеченный как TreatAsSafe, может быть вызван из прозрачного кода. Я также отмечал, что поскольку в инфраструктуре настольных систем прозрачность применяется только в пределах сборки, всему общедоступному ключевому неявно назначен атрибут TreatAsSafe. В CoreCLR создан новый атрибут, SafeCritical (который по-настоящему называется SecuritySafeCritical), для описания ключевого кода, который можно вызывать из прозрачного. Модель прозрачности не только внутри, но и между сборками, в результате чего любой ключевой код не может быть вызван из прозрачного кода.

С помощью атрибута SafeCritical можно решить задачу видимости ключевого кода из прозрачного. Во-первых, весь ключевой код ограничен — для общедоступного кода исключений нет, так что объявление кода ключевым означает, что его нельзя вызвать из прозрачного кода. Кроме того, атрибут TreatAsSafe имеет смысл только в том случае, если атрибут «Critical» уже установлен — требование двух атрибутов сразу может весть к ошибкам, потому что присвоить атрибут TreatAsSafe можно прозрачному методу, хотя этот атрибут и не будет ни на что влиять. Наконец, имея только одним способ задать уровень безопасности, наличие ключевого кода еще больше упрощает модель.

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



Правила наследования

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

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

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

В заключение можно сказать, что в модели прозрачности Silverlight любой код по умолчанию считается прозрачным. Все приложения Silverlight совершенно прозрачны. Игнорируюся любые пометки, указывающие на обратное. Кроме того, наличие предопределенного набора прав означает, что единственным необходимым для CoreCLR механизмом является механизм прозрачности. На рис. 3 приведены все три категории кода, их свойства и возможности.

Рис. 3. Сравнение прозрачного, безопасного ключевого и ключевого кода


Знакомство с прозрачностью в Transparency

Основное преимущество модели прозрачности Silverlight проявляется в простоте обеспечения безопасности платформы Silverlight. Поскольку Silverlight — закрытая платформа, то проверенных библиотек от независимых производителей для нее еще не выпущено. В результате взаимодействие разработчика с моделью прозрачности Silverlight ограничено прикладными программными интерфейсами, которые разрешено ему вызывать, и которые являются либо прозрачными, либо безопасными ключевыми, поскольку все приложения разработчика могут быть только прозрачными. В то же время .NET Framework закрытой платформой не является, для него поставляются дополнительные библиотеки, и их разработчики только выиграют от более простой модели обеспечения безопасности.

К сожалению, разработчики смогут воспользоваться этими преимуществами только в следующем основном выпуске инфраструктуры для настольных систем. Модель прозрачности, предложенная нами в .NET Framework версии 2.0, помогла существенно упростить анализ и определение степени безопасности библиотек APTCA; модель прозрачности Silverlight является следующим шагом вперед, гарантируя то, что раньше нужно было подтверждать тщательным анализом кода. В будущем мы планируем внедрить более строгие правила прозрачности кода, применения атрибута «безопасный ключевой код» и правила наследования в .NET Framework. Также предполагается, что эти правила будут доступны и сторонним разработчикам.

Одним из главных отличий инфраструктуры для настольных компьютеров от инфраструктуры Silverlight является то, что большинство приложения для настольных компьютеров создавалось для полностью проверенной среды исполнения, тогда как в Silverlight приложения строились с расчетом на частично проверенную среду. Большинство приложений для настольных компьютеров Most desktop applications can do anything they want and would continue to be able to do so—they won't have to think about Transparency at all. С точки зрения внедрения эти приложения будут рассматриваться как критические.

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

Использование более строгих правил прозрачности также приведет к тому, что увеличится производительность и безопасность работы частично проверенных приложений. В .NET Framework, использующем модель прозрачности версии 2.0, если прозрачный код содержит или вызывает небезопасный или непроверяемый код, то происходит подстановка требования на разрешение UnmanagedCodePermission, после чего начинается ресурсоемкий анализ стека. Анализ стека обычно заканчивается неудачей, потому что большинство «песочниц» не имеет полномочий выдавать такое разрешение.

Новая модель прозрачности предполагает, что в этом случае произойдет ошибка, как и в CoreCLR, никаких требований подставлено не будет и, следовательно, анализ стека не потребуется. By bringing the new Transparency model to the next version of the desktop framework, we hope to make it easier to develop APTCA libraries as well as improve the experience around developing partially trusted applications.

Эндрю Дэй (Andrew Dai)