Криптография в ASP.NET

169

Ранее вы узнали, как идентифицировать пользователей с помощью нескольких поддерживаемых механизмов аутентификации, и как реализовать авторизацию этих пользователей в своих приложениях. ASP.NET поддерживает такие развитые службы, как Membership API и Roles API, которые помогают реализовать эту функциональность.

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

В состав .NET входит многофункциональный интерфейс CryptoAPI, предназначенный для решения широкого диапазона криптографических задач - таких как создание хешей различного типа (MD5, SHA1 и т.п.) и реализация наиболее важных симметричных и асимметричных алгоритмов шифрования. А если этого недостаточно, то .NET Framework включает отдельные функции для защиты секретной информации на локальной машине или для каждого пользователя посредством полностью управляемых оболочек интерфейса Windows Data Protection API (DPAPI).

Шифрование данных: соображения конфиденциальности

Ранее было показано как использовать хеширование для защиты паролей с помощью методов класса FormsAuthentication. Благодаря хешированию, сохраняется контрольная сумма ("отпечаток пальца") исходных данных, но не сами данные. В результате повернуть вспять процесс хеширования, чтобы восстановить исходные данные, возможности нет. Все, что можно сделать - хешировать новые данные и выполнить сравнение.

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

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

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

Пространство имен Cryptography в .NET

Все необходимые классы для шифрования и расшифровки информации в приложениях можно найти в пространстве имен System.Security.Cryptography. Кроме того, там находятся все основные классы для создания различного рода хешей. Если вы обратитесь к дополнительной сборке System.Security.dll, то получите в свое распоряжение еще более совершенную функциональность обеспечения безопасности - такую как API-интерфейс для модификации Windows ACL (пространство имен System.Security.AccessControl), DPAPI и классы для создания кодов аутентификации на основе хешированных ключей (key-hashed message authentication code - HMAC).

В таблице ниже описаны категории этих классов:

Категории классов безопасности из пространства имен System.Security.Cryptography
Категория Описание
Алгоритмы шифрования

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

Вспомогательные классы

Если нужно создавать криптографически строгие случайные числа, то вспомогательные классы для этого находятся в пространстве имен System.Security.Cryptography. Вспомогательные классы предназначены для взаимодействия с криптографической системой Windows (CryptoAPI)

Сертификаты X509

В пространстве имен System.Security.Cryptography.X509Certificates находятся все необходимые классы для работы с сертификатами X509 и классы для доступа к хранилищу сертификатов Windows

Сигнатуры и шифрование XML

Полную поддержку сигнатур XML и стандартов шифрования можно найти в пространстве имен System.Security.Cryptography.Xml. Классы в этом пространстве имен используются для шифрования и подписи документов XML в соответствии со стандартами, опубликованными консорциумом W3C

CMS/PKCS#7

Платформа включает управляемую поддержку упакованных согласно CMS/PKCS сообщений непосредственно от вызовов неуправляемого кода. (CMS - Cryptographic Message Syntax (Синтаксис криптографических сообщений), a PKCS - Public-Key Cryptography Standard (Стандарт шифрования с открытым ключом).)

В веб-мире сертификаты X509 играют важную роль. Они устанавливают коммуникации SSL и выполняют аутентификацию с помощью сертификатов для защиты трафика между веб-сервером и его клиентами. Сертификат X509 - это двоичный стандарт инкапсуляции ключей для алгоритмов асимметричного шифрования вместе с сигнатурой специальной организации, издающей сертификаты (обычно такие организации называются центрами сертификации).

Для простых SSL-соединений доступ к хранилищу сертификатов не требуется. Но если в коде планируется обращаться к веб-службам или веб-приложениям, расположенным на другом сервере, который требует аутентификации сертификатом X509, то приложение должно прочитать сертификат из хранилища сертификатов Windows и добавить его к веб-запросу (или к прокси веб-службы) перед отправкой этого запроса. Для этой цели в пространстве имен System.Security.Cryptography.X509Certificates предусмотрено несколько классов:

X509Certificate и X509Certificate2

Эти классы инкапсулируют сертификаты X509. Они позволяют загружать сертификаты из разных хранилищ, таких как файловая система, и обеспечивают доступ к свойствам сертификата. Класс X509Certificate изначально появился в самых ранних версиях .NET Framework. Класс X509Certificate2 - это расширение класса X509Certificate, включающее ряд дополнительных методов и свойств.

X509Store

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

X509CertificateCollection

Это простой класс, предоставляющий коллекцию экземпляров X509Certificate и X503Certificate2, хранящих одиночные сертификаты. X509Store позволяет извлекать как список сертификатов, так и одиночные сертификаты, на основе одного из уникальных идентификаторов (таких как ключ субъекта сертификата, имя субъекта или хеш).

Прочитать сертификат из хранилища и присвоить его веб-запросу можно следующим образом:

X509Certificate2 Certificate = null;

// Прочитать сертификат из хранилища
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);

try
{
    // Попытаться найти сертификат на основе его общего имени
    X509Certificate2Collection Results = store.Certificates.Find(
        X509FindType.FindBySubjectDistinguishedName, "CN=PROFESSORWEB", false); 
    
    if (Results.Count == 0)
        throw new Exception ("He удалось найти сертификат!"); 
    else
        Certificate = Results[0];
}
finally
{
    store.Close();
}

В этом коде открывается персональное хранилище сертификатов локальной машины с использованием класса X509Store. Затем предпринимается попытка найти в этом хранилище сертификат с именем субъекта "CN=PROFESSORWEB". Здесь используется общий синтаксис именования, который, возможно, знаком по системам каталогов LDAP.

В Windows поддерживается несколько типов хранилищ сертификатов, которые называются расположениями хранилищ (store locations). Хранилище локальной машины, например, доступно всем приложениям, запущенным с определенными привилегиями на этой локальной машине. Можно создать отдельное хранилище для каждой Windows-службы на машине, и каждый пользователь может иметь отдельное хранилище сертификатов. Сертификаты безопасно хранятся в этих хранилищах.

В то время как хранилище локальной машины шифруется ключом, управляемым локальным центром безопасности данной машины, пользовательское хранилище шифруется ключом, который хранится в профиле пользователя. В пределах одного местоположения хранилищ Windows различает хранилища, используемые для разных целей. Наиболее важными хранилищами являются персональное ("my") хранилище и Trusted Root Certification Authorities (Доверительный корневой центр сертификации).

Обычно хранилище "my" содержит все сертификаты, используемые приложениями (и пользователями, если речь идет о пользовательском хранилище), в то время как хранилище Trusted Root Certification Authorities содержит сертификаты для центров, издающих сертификаты. Примером известного центра сертификации, у которого можно приобретать сертификаты, является VeriSign.

Если сертификат помещается в хранилище Trusted Root Certification Authorities, то тем самым утверждается, что любой сертификат, изданный этим центром, заверен системой и потому может использоваться любым приложением безо всяких опасений. Другие сертификаты по умолчанию не являются доверенными, и потому помечаются специальным флагом. Конечно, для таких критичных операций, как аутентификация или настройка SSL на сервере, необходимо применять только допустимые сертификаты, изданные доверительным центром, поскольку любые другие сертификаты представляют потенциальный риск нарушения безопасности.

В веб-приложениях ASP.NET должно использоваться либо хранилище локальной машины, либо хранилище учетной записи службы (представляющее собой пользовательское хранилище служебной учетной записи, от имени которой запущена системная служба Windows). Таким образом, представленный выше код открывает хранилище с флагом StoreLocation.LocalMachine. Вторым возможным флагом для этой опции является StoreLocation.CurrentUser, который открывает хранилище текущего пользователя или учетной записи службы Windows. Поскольку данный сертификат - это сертификат "использования", он читается из персонального хранилища.

Все сертификаты, находящиеся в хранилище, можно просмотреть, открыв консоль управления Microsoft Management Console и затем запустив оснастку Certificates (Сертификаты), как показано на рисунке ниже:

Оснастка Certificates консоли управления

Чтобы открыть эту консоль, запустите консоль управления (mmc.exe) и выберите пункт меню File --> Add/Remove Snap In (Файл --> Добавить или удалить оснастку). В открывшемся диалоговом окне выберите Certificates в списке доступных оснасток и добавьте ее в список выбранных. Выберите хранилище, которое хотите отобразить в оснастке. После этого закройте диалоговое окно, и оснастка Certificates отобразит в консоли управления все хранилища и сертификаты в этих хранилищах для выбранной учетной записи.

Для создания тестовых сертификатов служит команда makecert.exe. Например, следующая команда создает сертификат в персональном хранилище на локальной машине:

makecert -ss my -sr LocalMachine -n "CN=PROFESSORWEB"

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

System.Net.HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(url);
Request.ClientCertificates.Add(Certificate);
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();

Для того чтобы приведенный код компилировался, понадобится импортировать пространство имен System.Net в файл кода. Этот код полезен в ситуации, когда приложению нужно извлекать данные из другого веб-приложения или отправлять данные другому веб-приложению с использованием HTTP-запросов GET либо POST, а другое приложение требует аутентификации с помощью сертификатов.

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

Ниже показано, как создавать случайные числовые значения с помощью класса RandomNumberGenerator:

byte[] RandomValue = new byte[16];
RandomNumberGenerator RndGen = RandomNumberGenerator.Create();
RndGen.GetBytes(RandomValue);
ResultLabel.Text = Convert.ToBase64String(RandomValue);

За дополнительной информацией о генераторе случайных чисел обращайтесь в документацию Windows по поставщику Cryptographic Service Provider, поскольку этот класс представляет собой оболочку встроенной реализации.

Пройди тесты
Лучший чат для C# программистов