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

1) Разработка технического задания.

2) Разработка программы:

- кодирование;

- отладка;

- тестирование;

3) Разработка документации.

4) Внедрение.

5) Поддержка.

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

В таблице 1 в первой колонке перечислены свойственные платформе .Net уязвимости, во второй примеры функций, которые обладают этими уязвимостями, а в третьей механизмы по закрытию данной уязвимости. Здесь не указана уязвимость переполнения буфера, так как платформе .Net она не свойственна.

Таблица №1. Уязвимости платформы .Net

Уязвимость Пример функции Механизмы защиты от уязвимости
Ошибки канонизации Работа с файлами - проверка пути на допустимые символы

- получение обсолютного пути файла, перед обращением к нему(System.IO.Path.GetFullPath для Windows, Request.MapPath для ASP.Net)

- настройка ACL, CAS

Межсайтовое кодирование Форум или иной обмен информации между пользователя в формате HTML - очистка данных введенных пользователем от кода HTML (HttpUtility.HTML.Encode())

- ограничение набора используемых тэгов HTML

- атрибут ValidateRequest защиты ASP.Net

Вставка SQL Работа с базой данных - настройка разрешений SQL

- использование хранимых процедур

- использование параметризированных команд SQL

- очистка вводимых пользователем данных от символом апострофа и процента

Отказ в обслуживании Клиент-серверное взаимодействие - аутентификация и авторизация до выделения ресурсов используя средства системы, базы данных, платформы .Net либо ASP.Net

- закрытие подключения при генерировании ошибки. Для отловки ошибки использовать блок try catch

- выявление подозрительной активности и переход в более безопасный режим либо блокирование

Использование посредника Передача данных по сети - шифрование данных передаваемых по сети, использую протоколы SSL, IPSec, HTTPS

- фильтрация пакетов маршрутизатором

- фильтрация пакетов аппаратным сетевым экраном

- фильтрация пакетов программным сетевым экраном

Взлом паролей Аутентификация пользователей с помощью паролей - ограничение попыток число ввода пароля

- выявление подозрительной активности и переход в более безопасный режим либо блокирование

Криптографический взлом Шифрование -использование надежных методов шифрования, хеширования и цифровой подписи (классы rijndael, RSACryptoServiceProvider, MD5CryptoServiceProvider, SHA512Managed и другие)

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

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

Программу необходимо строить по трем принципам:

1) Принцип разделения привилегий.  Пользователей надо классифицировать, например, по членству в группах пользователей Windows, роли в базе данных или собственным способами. У каждой группы пользователей должны быть свои привилегии. После разделения привилегий нужно проводить аутентификацию и авторизацию пользователей. Система .Net имеет мощные встроенные средства для проведения этих операций (Windows.Identity, Windows.Principal, PrincipalPermission, интерфейсы System.Security.Principal.IIdentity, System.Security.Principal.IPrincipal, классы  System.Security.Principal.GenericIdentity, System.Security.Principal. GenericPrincipal, средства аутентификации и авторизации ASP.Net). Разделение привилегий можно также проводить не только на уровне пользователей, но и на уровне приложений. Для этого в .Net имеет средство CAS позволяющее проводить разграничение доступа приложений .Net не хуже чем пользователей.

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

3) Принцип уменьшения поверхности атаки. Приложение должно предоставлять пользователю простейший из возможных интерфейсов и предоставлять пользователям (и другим приложениям) минимум способов ввода запросов.

Любые данные введенные пользователем должны быть тщательно проверены и пройти три этапа обработки, перед тем как использоваться:

1) Ограничение. Все данные не соответствующие типу, длине, формату и диапазоны должны быть отклонены. Для этого можно использовать строгое определение типа, регулярные выражения, элементы управления ASP.Net, свойство String.Length, сравнение типизированных данных.

2) Отклонение. Данные содержащие известные вредоносные значения и фразы должны быть отклоненты. Для этой цели удобно использовать регулярные выражения.

3) Очистка. Символы, имеющие особое значение для приложения должны быть очищены с помощью функции замены.

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

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

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

При разработке приложения следует избегать вызова внешних компонентов, так как не следует доверять стороннему разработчику. Особенно нужно избегать вызова неуправляемого кода, на который не действуют разграничения CAS и который может содержать уязвимости переполнения буфера. Это существенно снижает уровень безопасности. Если же это необходимо, такие компоненты следует вызывать с наименьшими привилегиями и проверять хэш компонента перед вызовом. Хранимое значение хэш должно быть защищено (например, подписью сборки).

Для тестирования программы существуют разные подходы. Разработчик может использовать упреждающее тестирование, создавая методы тестирования методов, до написания самых методов. Для тестирования модулей можно использовать собственные средства или средства сторонних компаний, например утилиту NUnit, FxCop, сетевой монитор, Application Center Test, Permission View, PEVerify. Если в ходе проведения тестирования были обнаружены недостатки, их нужно устранить и провести тестирование заново.

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

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

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

Заключительным этапом в последовательности жизненного цикла програмного обеспечения является поддержка. После внедрения приложения необходимо поддерживать его защиту, проводя мониторинг атак, выполняя аудит, обнаруживая не закрытые уязвимости приложения, а также выпуская исправления системы безопасности и устанавливая их. Для аудита и мониторинга можно использовать средства Windows, такие как аудит системы и аудит ресурсов, средства базы данных, например, монитор активности, а также средства программного обеспечения, сетевого монитора и др.

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

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