Пятница, 29.03.2024, 17:18
Мой персональный сайт Добрым людям smart & sober

Главная Регистрация Вход
Приветствую Вас, Гость · RSS
Калькулятор


Меню сайта
Календарь
«  Декабрь 2010  »
ПнВтСрЧтПтСбВс
  12345
6789101112
13141516171819
20212223242526
2728293031


Форма входа


Архив записей
Мини-чат


Категории раздела


Наш опрос
В чем заключается ваш смысл жизни
Всего ответов: 154
 
Главная » 2010 » Декабрь » 28 » Пуленепробиваемый C#: основные правила создания безопасного кода
02:32
Пуленепробиваемый C#: основные правила создания безопасного кода
Александр Эккерт

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

Введение

Увы и ах — технологии .NET прочно вошли в нашу жизнь, и на сегодняшний день разработчики C# пользуются неслыханной популярностью на рынке труда. Легкий в изучении и освоении язык дал программисту неслыханную свободу действий и при этом позволил расширить круг тех лиц, которые стали гордо именовать себя "программистами". Столь низкий "порог вхождения в специальность" обусловил тот факт, что начинающие (и не очень) программисты не стали уделять должного внимания безопасности своего кода. Но обо всем по порядку.

У общеязыковой исполняющей среды (common language runtime, CLR) в .NET Framework есть своя модель безопасного выполнения кода, независимая от ограничений операционной системы, в которой она работает. Более того, в отличие от старой модели защиты на основе участников безопасности (principal-based security), CLR реализует политику, исходя из того, откуда поступает код, а не из того, кто являет ся его пользователем. Эта модель защиты по правам доступа кода (code access security) имеет больший смысл в современных условиях, поскольку немалая часть кода устанавливается через интернет, и даже доверенный пользователь (trusted user) не знает, какой код действительно безопасен. Все это реализовано в пространстве имен System.Security. "Ээээээ, так ты об этом..." — разочарованно вздохнет читатель, который, наверняка, вдоль и поперек изучил все те фичи, которые .NET предлагает программисту для реализации его злобных замыслов. Спешу огорчить — о System.Security мы сегодня как раз разговаривать не будем. Это скучно :). Вместо этого мы попробуем взглянуть на проблему "безопасного кода" с другой стороны — с точки зрения того, в чьи хорошие (или не очень) руки он попадет. Вне зависимости от того, что предоставляет интерфейс твоей программы: просто складывает два числа или же управляет атомной электростанцией.

Что может CLR?

Общеязыковая исполняющая среда (common language runtime, CLR) и Microsoft .NET Framework предоставляют всем приложениям с управляемым кодом защиту на основе признаков — это так называемая evidence-based security. В большинстве случаев при написании кода обеспечивать защиту явным образом не требуется. Тем не менее, я попытаюсь кратко рассмотреть вопросы безопасности, которые тебе, как мегакрутому программисту, возможно, понадобится учитывать при написании кода, и описать те принципы классификации компонентов, позволяющие определить, что нужно предпринять для гарантированной защиты кода.

Для защиты управляемого кода используются две технологии:

  • защита на основе признаков (evidence-based security) позволяет определять, какие разрешения следует предоставлять коду;
  • защита по правам доступа кода (code access security) позволяет проверять, весь ли код в стеке имеет необходимые разрешения на выполнение каких-либо действий.

Эти две технологии связаны между собой концепцией разрешений.

По признакам и политике безопасности, устанавливаемой администратором, система защиты определяет, какие разрешения могут быть выданы коду. Программа сама может запрашивать какое-либо разрешение, влияя на состав окончательного набора разрешений. Запрос разрешения выражается в виде объявления на уровне сборки с синтаксисом пользовательских (custom) атрибутов. Однако, в любом случае, код не может получить более широкие или ограниченные разрешения, чем это предписано политикой безопасности. Разрешение предоставляется только раз и определяет права всего кода в сборке. Для просмотра и изменения политики безопасности используется инструмент настройки .NET Framework (Mscorcfg.msc).


Mscorcfg.msc

Планируем боевые действия

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

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

Ihre ausweiss, bitte!: выдача разрешений

Защита на основе признаков базируется на предположении, что высокий уровень доверия (с широкими полномочиями) присваивается лишь коду, заслуживающему этого самого доверия, а злонамеренный код является "малодоверяемым" или вообще не имеет разрешений. В соответствии с политикой по умолчанию в .NET Framework разрешения выдаются на основе зон (так, как они определены в Microsoft Internet Explorer). Ниже приведено упрощенное описание этой политики "по умолчанию":

  • Зона локального компьютера (например, C:\app.exe) является полностью доверяемой. Предполагается, что пользователи помещают на свой компьютер только код, которому они доверяют, и что большинство пользователей не собираются разбивать свой жесткий диск на области с разной степенью доверия. По существу, этот код может делать все что угодно, поэтому от злонамеренного кода, находящегося в этой зоне, никакой защиты нет. Честно говоря, по моему скромному мнению, именно это предположение является одной из огромных дыр в архитектуре безопасности Windows, что приводит к появлению таких извратов, как UAC, DEP, рандомизация стека, сандбоксов и пр.
  • Зона интернета (например, http://www.microsoft.com). Коду из этой зоны предоставляется очень ограниченный набор разрешений, который не опасно предоставить даже злонамеренному коду. Обычно этому коду нельзя доверять, поэтому его можно безопасно выполнять только с очень узкими разрешениями, с которыми он не сумеет нанести ущерб:
  • WebPermission — доступ к серверу сайта, с которого получен код;
  • FileDialogPermission — доступ только к файлам, специально указанным пользователем;
  • IsolatedStorageFilePermission — постоянное хранилище, ограниченное пределами веб-сайта;
  • UlPermission — возможность записи информации в окно пользовательского интерфейса;
  • Зона интрасети (например \\UNC\share). Код из этой зоны выполняется с чуть большими разрешениями, чем код из интернета, но среди них все равно нет таких, которые предоставляли бы широкие полномочия;
  • FilelOPermission — доступ только для чтения к файлам каталога, из которого загружен код;
  • DNSPermission — допускается разрешение DNS-имен в IP-адреса;
  • FileDialogPermission — доступ только к файлам, специально указанным пользователем;
  • Isolated StorageFilePermission — постоянное хранилище (с меньшими ограничениями);

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

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

Не влезай - убьет!

Какие разрешения несут в себе потенциальную опасность? Для выполнения некоторых защищенных операций .NET Framework предоставляет разрешения, потенциально позволяющие обойти систему защиты. Эти опасные разрешения следует предоставлять только коду, заслуживающему доверия, и только при абсолютной необходимости. Обычно, если злонамеренный код получает такие разрешения, то защититься нельзя. К опасным разрешениям относятся:

[Security Permission]:

  • Unmanaged Code — позволяет управляемому коду вызывать неуправляемый код, что зачастую весьма опасно;
  • Skip Verification — код может делать что угодно без всякой верификации;
  • ControlEvidence — управление признаками позволяет обмануть систему защиты;
  • ControlPolicy — возможность изменять политику безопасности позволяет отключить защиту;
  • SerializationFormatter — за счет сериализации можно обойти управление доступом;
  • ControlPrincipal — возможность указывать текущего пользователя позволяет обходить защиту на основе ролей;
  • ControlThread — возможность манипуляций с потоками опасна, так как с ними связано состояние защиты;

[ReflectionPermission]:

  • MemberAccess — позволяет отключать механизм управления доступом (становится возможным использование закрытых членов).

Защита доступа к методам

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


Утилита графической настройки прав доступа к коду GuiCaspol

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

  • Ограничь область доступности классом, сборкой или производными классами (если им можно доверять). Это простейший способ ограничения доступа к методу. Замечу, что вообще-то производные классы могут быть менее доверяемыми, чем класс-предок, но в некоторых случаях они используют ту же идентификацию, что и надкласс. В частности, ключевое слово protected не подразумевает доверия, и его необязательно нужно использовать в контексте защиты;
  • Разрешай вызов метода только вызывающим с определенной идентификацией (обладающим заданными вами признаками);
  • Разрешай вызов метода только тем, у кого есть требуемые разрешения.

Аналогичным образом декларативная защита позволяет контролировать наследование классов. С помощью InheritanceDemand можно потребовать наличия определенной идентификации или разрешения от:

  • Всех производных классов;
  • Производных классов, переопределяющих те или иные методы.

Защищаем доступ к методу или классу

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

1. Команда sn -k создает пару из закрытого и открытого ключа. Закрытая часть нужна, чтобы подписать код строгим именем (strong name), и хранится в безопасном месте издателем кода. Если она станет известной, указать твою подпись в своем коде сможет кто угодно.

sn -k keypair.dat
csc/r:App1.dll /a. keyfile: keypair.dat App1.cs
sn -p keypair.dat public.dat
sn -tp public.dat >publichex.txt
[StrongNameldentityPermissionAttribute ( SecurityAction . LinkDemand , PublicKey="...hex...",Name="App1", Version="0. 0.0.0")]
public class MyClass

2. Команда esc компилирует и подписывает Appl, предоставляя ему доступ к защищенному методу.

3. Следующие две команды sn извлекают из пары открытый ключ и преобразуют его в шестнадцатеричную форму.

4. Во второй половине показанного исходного кода содержится фрагмент защищаемого метода. Пользовательский атрибут (custom attribute) определяет строгое имя и в шестнадцатеричном формате вставляет открытый ключ, полученный командой sn, в атрибут PublicKey.

5. В период выполнения Appl имеет необходимую подпись со строгим именем и может использовать MyClass. В этом примере для защиты API-элемента применяется атрибут LinkDemand.

В примере, показанном ниже, частично доверенному коду запрещается обращаться к классам и методам (а также к свойствам и событиям). Когда такие объявления применяются к классу, защищаются все методы, свойства и события этого класса. Однако декларативная защита не влияет на доступ к полям. Кроме того, учти, что требования к связи (link demands) защищают только от непосредственно вызывающего кода — возможность атак с подменой сохраняется.

[System.Security.Permissions. PermissionSetAttribute(System.Security. Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")]
[System.Security.Permissions. PermissionSetAttribute(System.Security. Permissions.SecurityAction.LinkDemand, Name="FullTrust")]
public class YourClass{...}


Утилита ручной настройки прав доступа к коду

Приемы безопасного кодинга

Запрос разрешений — отличный способ обеспечить поддержку защиты в разрабатываемом коде. Он позволяет запрашивать минимальные разрешения, необходимые для выполнения кода, и гарантировать, что код не получит разрешений больше, чем нужно. Например:

[assemblyiFilelOPermissionAttribute (SecurityAction.RequestMinimum, Wrlte="C:\\test.tmp")]
[assembly:РеrmissionSet(SecurityAction.RequestOptional. Unrestricted=false)]
... SecurityAction.RequestRefused ...

В этом примере системе сообщается, что код не должен запускаться, пока не получит разрешение на запись в C:\test.tmp. Если одна из политик безопасности не предоставляет такое разрешение, генерируется исключение PolicyException, и код не запускается. Ты должен убедиться в том, что коду выдается нужное разрешение, и тогда тебе не придется беспокоиться об ошибках из-за нехватки разрешений. Кроме того, здесь система уведомляется о том, что дополнительные разрешения нежелательны. Иначе код получит все разрешения, предусмотренные политикой безопасности. Лишние разрешения не принесут вреда, но, если в системе безопасности есть какая-то ошибка, уменьшение числа разрешений, выдаваемых коду, может прикрыть брешь в защите. Таким образом, если код обладает разрешениями, которые ему не нужны, возможны проблемы с безопасностью. Еще один способ ограничить количество привилегий, предоставляемых коду — явно перечислить разрешения, от которых следует отказаться. Отказ от разрешений осуществляется объявлением необязательности разрешений и исключением конкретных разрешений из запроса.

Заключение

Уфф! На тему обеспечения безопасности твоего кода можно говорить бесконечно. В рамках этой статьи я постарался упомянуть только самое важное и, на мой взгляд, интересное. Иными словами, то, что должно помочь тебе сделать свои приложения непробиваемыми. В общем, да пребудет с тобой Сила!


Просмотров: 743 | Добавил: Breger | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Copyright MyCorp © 2024