Реализация аутентификации с помощью форм
69ASP.NET --- Безопасность в ASP.NET --- Реализация аутентификации с помощью форм
Чтобы использовать аутентификацию с помощью форм в приложении, необходимо выполнить перечисленные ниже шаги:
Сконфигурируйте аутентификацию с помощью форм в файле web.config.
Настройте IIS для разрешения анонимного доступа к виртуальному каталогу, а также сконфигурируйте ASP.NET для ограничения анонимного доступа к веб-приложению.
Создайте собственную страницу входа, которая будет принимать и проверять имя и пароль пользователя, а затем взаимодействовать с инфраструктурой аутентификации с помощью форм для создания билета.
Все эти шаги более подробно описаны в последующих разделах.
Конфигурирование аутентификации с помощью форм
Вы должны надлежащим образом сконфигурировать аутентификацию с помощью форм в файле web.config. Ранее упоминалось, что каждый файл web.config включает раздел конфигурации <authentication>. Аутентификация с помощью форм будет работать, если в этом разделе указать для атрибута mode значение Forms:
<system.web>
<authentication mode="Forms">
<!-- Опции конфигурации -->
</authentication>
</system.web>
Конфигурация <authentication> ограничена только файлом web.config высшего уровня приложения. Если атрибут mode установлен в Forms, то ASP.NET загружает и активизирует модуль FormsAuthenticationModule, который выполнит большую часть работы. В приведенной выше конфигурации в основном используются установки по умолчанию для аутентификации с помощью форм, которые жестко закодированы в исполняющей среде ASP.NET. Чтобы переопределить эти настройки, добавьте установки в раздел <system.web> файла machine.config. Эти настройки можно переопределить в приложении, добавив свои установки в дочерний дескриптор <forms> этого раздела. В следующем фрагменте кода показан полный набор опций дескриптора forms:
<authentication mode="Forms">
<forms name="MyCookieName"
loginUrl="MyLogin.aspx"
timeout="20"
slidingExpiration="true"
cookieless="AutoDetect"
protection="All"
requireSSL="false"
enableCrossAppRedirects="false"
defaultUrl="Default.aspx"
domain="www.mydomain.com"
path="/" />
</authentication>
В приведенном примере для свойства domain устанавливается значение, представляющее ваш домен. Однако обычно при разработке и отладке приложения его можно запускать либо на локальном сервере, либо на тестовом сервере внутри корпоративной сети (например, используя URL-адрес http://localhost:<порт> в случае интегрированного веб-сервера разработки). В этом случае используемый для доступа к приложению URL-адрес отличается от реального домена. Поэтому аутентификация с помощью форм не будет работать, поскольку она сравнивает имя cookie-домена с URL-адресом, применяемым для доступа к веб-серверу.
Если нужно протестировать аутентификацию домена между несколькими приложениями, рекомендуется установить для свойства domain имя локальной машины или тестовой машины и обращаться к приложению с указанием этого имени машины вместо localhost (например, http://имя_машины:<порт> вместо http://localhost:<порт>).
Выше свойства конфигурации были перечислены в порядке, в котором они используются в большинстве случаев. В таблице ниже описаны подробности этих свойств и их настройки по умолчанию:
Опция | Значение по умолчанию | Описание |
---|---|---|
name | .ASPXAUTH |
Имя cookie-набора HTTP для использования в аутентификации (по умолчанию .ASPXAUTH). Если множество приложений работают на одном веб-сервере, каждому cookie-набору безопасности каждого приложения должно быть назначено уникальное имя |
loginUrl | login.aspx |
Определяет, на какую страницу должен быть перенаправлен пользователь, чтобы войти в приложение. Это может быть страница в корневой папке приложения или же в каком-то подкаталоге |
timeout | 30 |
Период времени в минутах действительности cookie-набора. ASP.NET обновит cookie-набор при приеме запроса, когда истечет половина времени действия cookie-набора. Устаревание cookie-наборов - важное соглашение. Если cookie-наборы будут устаревать слишком быстро, пользователю придется часто заново регистрироваться в системе, от чего пострадает удобство использования приложения. Если же они будут устаревать редко, то возникает опасность их хищения и нерегламентированного использования |
slidingExpiration | false |
Этот атрибут включает и отключает скользящее устаревание cookie-наборов аутентификации. При включении устаревание cookie-наборов будет сбрасываться исполняющей средой при каждом запросе страницы пользователем. Это значит, что каждый запрос будет продолжать время жизни cookie-набора |
cookieless | UseDeviceProfile |
Позволяет указать, должна ли исполняющая среда использовать cookie-наборы для отправки билетов аутентификации с помощью форм клиенту. Возможные варианты: AutoDetect, UseCookies, UseUri и UseDeviceProfile. Эти настройки детально описаны далее в следующей таблице |
protection | All |
Позволяет указать уровень безопасности cookie-наборов аутентификации. Опция All шифрует и подписывает аутентифицирующие cookie-наборы. Другими возможными значениями являются None, Encryption (только шифрование) и Validation (только подпись) |
requireSSL | false |
Если установлено в true, это свойство дает тот эффект, что браузер просто не передает cookie-набор, если SSL не включен на веб-сервере. Таким образом, аутентификация с помощью форм работать не будет, если SSL не активизирован на веб-сервере |
enableCrossAppRedirects | false |
Разрешает переадресацию между разными приложениями на сервере при использовании аутентификации с помощью форм. Конечно, это имеет смысл, только если оба приложения полагаются на одно и то же хранилище удостоверений и используют одинаковый набор пользователей и ролей |
defaultUrl | Default.aspx |
Если FormsAuthenticationModule перенаправляет запрос от пользователя на страницу входа, то включает исходную запрошенную страницу при ее вызове. Таким образом, когда страница входа вернет управление, модуль сможет использовать этот URL для перенаправления на исходную запрошенную страницу после успешной проверки удостоверения пользователя. Но что если пользователь запросит страницу входа непосредственно? Эта опция указывает страницу для перенаправления в подобных случаях |
domain | пустая строка |
Указывает домен, для которого cookie-набор является допустимым. Переопределение этого свойства полезно, когда необходимо разрешить использование cookie-наборов для других приложений на вашем веб-сервере |
path | / |
Путь для cookie-наборов, указанный приложением. Рекомендуется применять значение по умолчанию (/), поскольку это предотвращает несовпадение регистра символов при пересылке в запросе |
Как показано в этой таблице, можно отключать проверку достоверности и шифрование cookie-наборов. Однако было бы резонно спросить - зачем может понадобиться отключать эту защиту? Единственный случай, в котором можно принять такое решение -когда пользователи не аутентифицируются для обеспечения безопасности, а только идентифицируются в целях персонализации. В таких случаях действительно не имеет значения, если пользователь представится другим пользователем, так что можно решить, что накладные расходы, связанные с шифрованием, дешифрацией и проверкой достоверности cookie-наборов аутентификации нанесет ущерб производительности, не предоставляя никаких выгод. Однако прежде чем избрать такой подход, стоит тщательно подумать, потому что его применение оправдано лишь в тех случаях, когда отказ от системы аутентификации действительно допустим.
Хранилище удостоверений в web.config
При использовании аутентификации с помощью форм существует выбор, где хранить удостоверения пользователей. Их можно хранить в определенном файле или в базе данных; в принципе хранить их можно где угодно, если предусмотрен код для проверки имени и пароля пользователя, вводимых на странице входа, по значениям, находящимся в хранилище. Проще всего хранить удостоверения пользователей непосредственно в файле web.config, в подэлементе <credentials> конфигурационного дескриптора <forms>, который был представлен ранее:
<authentication mode="Forms">
<forms name="MyCookieName"
loginUrl="MyLogin.aspx"
timeout="20">
<credentials passwordFormat="Clear">
<user name="Admin" password="12345" />
<user name="Alex" password="alexftw"/>
<user name="Elena" password="12fgap8"/>
</credentials>
</forms>
</authentication>
Во-первых, использование web.config в качестве хранилища удостоверений допустимо только для простых решений с несколькими пользователями. В более крупных сценариях необходимо применять Membership API, описанный позже. Во-вторых, пароли, сохраняемые в файле web.config, можно хешировать. Хеширование - это всего лишь однонаправленное шифрование паролей. Это значит, что пароли будут зашифрованы так, что их невозможно будет расшифровать.
Закрытие доступа анонимным пользователям
Как упоминалось ранее, для использования аутентификации ограничивать доступ к страницам не обязательно. Аутентификацию можно применять только для целей персонализации, так что анонимные пользователи будут видеть те же страницы, что и аутентифицированные пользователи (но в слегка измененном, персонализированном виде). Однако для демонстрации функциональности переадресации при аутентификации с помощью форм полезно разработать пример, закрывающий доступ анонимным пользователям. Это заставит ASP.NET перенаправлять анонимных пользователей на страницу входа.
Процесс авторизации детально описан позже, а пока будет использоваться простая техника запрета доступа всем пользователям, не прошедшим аутентификацию. Для этого необходимо добавить новое правило авторизации к элементу <authorization> в файле web.config, как показано ниже:
<system.web>
<!-- Другие настройки опущены -->
<authorization>
<deny users="?"/>
</authorization>
</system.web>
Знак вопроса (?) - это шаблонный символ, которому соответствуют все анонимные пользователи. Включая это правило в файл web.config, вы указываете, что анонимные пользователи не разрешены. Каждый пользователь должен быть аутентифицирован, и каждый запрос будет требовать присутствия билета аутентификации с помощью форм (в виде cookie-набора). Теперь, если вы непосредственно запросите страницу приложения, ASP.NET определит, что запрос не аутентифицирован, и попытается переадресовать его на страницу входа (что, возможно, вызовет ошибку, если эта страница не создана заранее).
В отличие от <authentication>, элемент <authorization> не ограничен файлом web.config, находящимся в корневом каталоге веб-приложения. Его можно создавать в любом подкаталоге, что позволяет устанавливать разные настройки авторизации для разных групп страниц.
Создание специальной страницы входа
Теперь понадобится разработать собственную страницу входа. Эта страница принимает имя и пароль пользователя и проверяет их по информации, записанной в хранилище удостоверений. Если удостоверения сохранены в файле web.config, это делается очень просто; проверить удостоверения, записанные в любом другом хранилище, таком как внешняя база данных, сложнее, но не намного.
Страница входа, которую необходимо разработать, должна включать части, показанные на рисунке ниже. Более того, потребуется предусмотреть код проверки удостоверений:
Страница ASP.NET, показанная на рисунке, содержит текстовые поля для ввода значений. Обратите внимание, что URL-адрес включает в себя исходную запрошенную страницу как параметр запроса. Этот параметр используется классом FormsAuthentication для перенаправления на исходную запрошенную страницу. Если она отсутствует, применяется страница, указанная в атрибуте defaultUrl дескриптора конфигурации <forms>.
На рисунке не видны элементы управления проверкой достоверности. Такие элементы управления особенно важны для того, чтобы обеспечить ввод пользователями только корректных значений имени и пароля. Вспомните, о чем шла речь ранее: никогда нельзя доверять пользовательскому вводу. Проверка достоверности реализует этот принцип за счет обеспечения ввода только допустимых значений.
Ниже показаны все элементы управления, находящиеся на странице входа:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MyLogin.aspx.cs" Inherits="MyLogin" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Войти в систему</title>
<style>
.center {
margin: 0 auto;
margin-top:15px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div style="text-align: center">
<b>Пожалуйста, войдите в систему</b><br />
<asp:Panel ID="MainPanel" runat="server" Width="440px" CssClass="center"
BorderColor="Silver" BorderStyle="Solid" BorderWidth="1px">
<br />
<table style="width:100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td style="height: 43px; width:30%; vertical-align:top">Имя пользователя:</td>
<td style="height: 43px; width: 70%">
<asp:TextBox ID="UsernameText" runat="server" Width="80%" />
<asp:RequiredFieldValidator ID="UsernameRequiredValidator" runat="server"
ErrorMessage="*" ControlToValidate="UsernameText" ForeColor="Red" />
<br />
<asp:RegularExpressionValidator
ID="UsernameValidator" runat="server"
ControlToValidate="UsernameText"
ErrorMessage="Некорректное имя пользователя"
ValidationExpression="[\w| ]*"
ForeColor="Red" />
</td>
</tr>
<tr>
<td style="height: 26px; width: 30%; vertical-align: top">Пароль:</td>
<td style="height: 26px; width: 70%">
<asp:TextBox ID="PasswordText" runat="server" Width="80%" TextMode="Password" />
<asp:RequiredFieldValidator ID="PwdRequiredValidator"
runat="server" ErrorMessage="*"
ControlToValidate="PasswordText" ForeColor="Red" />
<br />
<asp:RegularExpressionValidator ID="PwdValidator"
runat="server" ControlToValidate="PasswordText"
ErrorMessage="Некорректный пароль"
ValidationExpression='[\w| !"§$%&/()=\-?\*]*'
ForeColor="Red" />
</td>
</tr>
</table>
<br />
<asp:Button ID="LoginAction" runat="server" OnClick="LoginAction_Click" Text="Войти" /><br />
<asp:Label ID="LegendStatus" runat="server" EnableViewState="false" Text="" />
</asp:Panel>
</div>
</form>
</body>
</html>
Если при запуске этой страницы у вас возникнет ошибка UnobtrusiveValidationMode, значит вы используете .Net Framework версии 4.5 и вам нужно будет отключить этот режим в файле web.config:
<appSettings>
<add key="ValidationSettings:UnobtrusiveValidationMode" value="None" />
</appSettings>
Как уже упоминалось, элементы управления проверкой достоверности служат двум целям. Во-первых, элементы управления RequiredFieldValidator гарантируют, что как имя, так и пароль вводятся в правильном формате, содержащем только символы, допустимые для применения в именах и паролях. Во-вторых, элементы управления RegularExpressionValdiator обеспечивают, что в текстовые поля User Name (Имя пользователя) и Password (Пароль) будут введены только правильные значения. Например, имя пользователя может содержать только буквы, десятичные цифры и пробелы. Таким образом, выражение проверки достоверности выглядит так:
ValidationExpression="[\w| ]*"
Класс символов \w эквивалентен [a-zA-Z_0-9], а последующий пробел позволяет применять пробелы в именах пользователей. Пароль, например, может также содержать специальные символы. Таким образом, выражение проверки достоверности для поля пароля будет выглядеть несколько иначе:
ValidationExpression='[\w| !"§$%&/()=\-?\*]*'
Обратите внимание, что значение атрибута представлено в одиночных кавычках, т.к. двойная кавычка входит в список допустимых специальных символов. Более того, поскольку атрибут содержится в коде дескриптора (и потому является HTML-сущностью), & говорит о том, что в пароле также разрешено применять символ амперсанда (&). Элемент управления проверкой достоверности в действии показан на рисунке ниже:
Как видно на рисунке, при наличии элемента управления проверкой достоверности можно предотвратить ввод пользователями таких имен и паролей, которые могут привести к атакам внедрением SQL. В дополнение к применению параметризованных SQL-запросов, вы всегда должны использовать элементы управления проверкой достоверности, чтобы снизить риск подобного рода атак на приложения.
Последний шаг при создании страницы входа - написание кода проверки удостоверений по значениям, введенным пользователем. Необходимый для этого код должен быть добавлен в событие Click кнопки "Войти". Поскольку следующее событие Click использует удостоверения, хранящиеся в файле web.config, в этом случае проверка довольно проста:
protected void LoginAction_Click(object sender, EventArgs e)
{
Page.Validate();
if (!Page.IsValid) return;
if (FormsAuthentication.Authenticate(UsernameText.Text, PasswordText.Text))
{
// Создать билет, добавить cookie-набор к ответу и
// перенаправить на исходную запрошенную страницу
FormsAuthentication.RedirectFromLoginPage(UsernameText.Text, false);
}
else
{
// Имя и пароль пользователя неправильны
LegendStatus.Text = "Вы неправильно ввели имя пользователя или пароль!";
}
}
Поскольку аутентификация с помощью форм использует стандартные формы HTML для ввода удостоверений, имя и пароль пользователя передаются по сети в виде простого текста. Это представляет собой очевидный риск для безопасности - любой, кто перехватит сетевой трафик, сможет прочитать имена и пароли, введенные в форме входа. По этой причине настоятельно рекомендуется шифровать трафик между браузером и сервером с использованием SSL - по крайней мере, пока пользователь обращается к странице входа.
Более того, важно включить условие Page.IsValid в начало этой процедуры. Причина в том, что элементы управления проверкой достоверности по умолчанию применяют JavaScript для выполнения проверки на стороне клиента. При вызове Page.Validate() проверка достоверности происходит на стороне сервера. Это важно для браузеров, у которых поддержка JavaScript либо отключена, либо вообще отсутствует. Если не предусмотреть эту часть, то проверка достоверности не произойдет, если браузер не поддерживает JavaScript или же поддержка JavaScript отключена. Поэтому всегда обеспечивайте в своем коде выполнение проверки достоверности на стороне сервера.
Класс FormsAuthentication предоставляет два метода, использованные в этом примере. Метод Authenticate() проверяет указанное имя и пароль пользователя по тем именам и паролям, которые хранятся в файле web.config, и возвращает булевское значение, говорящее о том, что соответствие найдено. Имейте в виду, что методы FormsAuthentication являются статическими, поэтому нет нужды создавать экземпляр класса FormsAuthentication для их вызова - вы просто обращаетесь к ним через имя класса.
Если обнаружено соответствие для указанного удостоверения, можно вызвать метод RedirectFromLoginPage(). Этот метод решает сразу несколько задач:
Создает билет аутентификации пользователя.
Шифрует информацию, включенную в билет.
Создает cookie-набор для хранения зашифрованной информации билета.
Добавляет cookie-набор в ответ HTTP, который отправляется клиенту.
Перенаправляет пользователя на исходную запрошенную страницу (которая содержится в виде параметра в URL запроса страницы входа).
Второй параметр RedirectFromLoginPage() указывает на необходимость создания постоянных cookie-наборов. Постоянные cookie-наборы сохраняются на жестком диске пользователя и могут быть повторно использованы при последующих визитах.
И, наконец, если Authenticate() возвращает false, на странице отображается сообщение об ошибке. Такой отклик всегда полезен, однако следует убедиться, что он не ослабляет защиту. Например, очень часто разработчики создают страницы входа, которые выдают разные сообщения об ошибках в зависимости от того, ввел пользователь неправильное имя или правильное имя и неверный пароль. Обычно это плохая идея. Если злоумышленник пытается угадать имя пользователя и пароль, не стоит подсказывать ему, когда он находится на правильном пути.
Выход пользователя
Выход пользователя из аутентификации с помощью формы сводится к простому вызову метода FormsAuthentication.SignOut(). Вы можете создать кнопку выхода и добавить в обработчик щелчка на ней следующий код:
protected void SignOut_Click(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
В результате вызова метода SignOut() удаляется cookie-набор аутентификации. В зависимости от приложения, пользователя можно переадресовать на другую страницу, когда он выходит из приложения. Если пользователь потом запросит другую страницу с ограниченным доступом, этот запрос будет снова переадресован на страницу входа. Можно также переадресовать его туда сразу после вызова метода SignOut(). Или же можно воспользоваться методом Response.Redirect().
В сложном приложении страница входа может вообще не быть страницей. Вместо этого она может быть отдельной частью другой страницы - фреймом HTML или закодированным пользовательским элементом управления. Такой прием позволяет сохранять элементы управления входом и выходом видимыми на каждой странице приложения. Платформа Membership API включает в себя готовые к применению элементы управления, обеспечивающие упомянутую функциональность.
Хеширование паролей в web.config
Аутентификация с помощью форм поддерживает возможность хранения паролей в различных форматах. В разделе конфигурации <credentials> элемента <forms> формат пароля указывается атрибутом passwordFormat, который может принимать три возможных значения:
- Clear
Пароли сохраняются в виде простого текста в элементах <user> раздела <credentials>.
- MD5
В элементах <user> сохраняется хешированные версии паролей, и для хеширования используется алгоритм MD5.
- SHA1
В элементах <user> раздела <credentials> в файле web.config содержатся хешированные пароли, а для хеширования применяется алгоритм SHA1. Это значение по умолчанию.
При использовании хешированной версии паролей потребуется написать инструмент или некоторый код, который будет хешировать пароли и сохранять их в файле web.config. Это может быть код вроде приведенного ниже, включенный в некоторые административные приложения, либо отдельное приложение Windows, предназначенные для управления пользователями вашего приложения (которое должно затем выполняться на веб-сервере). Для сохранения пароля должен использоваться метод HashPassworsForStoringInConfigFile вместо передачи пароля в виде простого текста:
string hashed = FormsAuthentication.HashPasswordForStoringInConfigFile("12345", "SHA1");
Первый параметр указывает пароль в виде открытого текста, а второй - используемый алгоритм хеширования. Результатом вызова метода является хешированная версия пароля.
Этот результат должен быть сохранен в файле web.config (когда web.config служит хранилищем пользовательских учетных записей) или же в собственной базе данных пользователей (если она выбрана в качестве хранилища информации о пользователях).
Если вы хотите модифицировать пользователей, сохраненных в web.config, как показано ранее, то должны применять API-интерфейс конфигурирования .NET Framework. Редактировать этот раздел с помощью веб-ориентированного инструмента конфигурирования нельзя. В следующем фрагменте кода показано, как модифицировать этот раздел с помощью API-интерфейса конфигурирования. Этот код обычно реализуется как часть инструмента администрирования для управления вашим веб-приложением, который доступен только администраторам:
Configuration MyConfig = WebConfigurationManager.OpenWebConfiguration("./");
ConfigurationSectionGroup SystemWeb = MyConfig.SectionGroups["system.web"];
AuthenticationSection AuthSec =
(AuthenticationSection)SystemWeb.Sections["authentication"];
AuthSec.Forms.Credentials.Users.Add(
new FormsAuthenticationUser(UserText.Text, PasswordText.Text));
MyConfig.Save();
Для использования API конфигурирования нужно импортировать в приложение пространство имен System.Web.Configuration. Вдобавок необходимо удостовериться, что в проекте присутствует ссылка на сборку System.Configuration.dll (по умолчанию так оно и есть).
Разумеется, запускать этот код могут только привилегированные пользователи, такие как администраторы веб-сайта, и процесс, выполняющий его, должен иметь права записи в файл web.config. К тому же код подобного рода не должен входить в само веб-приложение. Он должен быть частью только инструмента администрирования.
Аутентификация с помощью форм без использования cookie-наборов
ASP.NET имеет встроенную поддержку аутентификации с помощью форм без cookie-наборов. Если вы не хотите, чтобы исполняющая среда использовала cookie-наборы, то настраиваете это с помощью атрибута cookieless дескриптора <form> в разделе <authentication>:
<system.web>
<authentication mode="Forms">
<forms ...
cookieless="AutoDetect">
...
</forms>
</authentication>
</system.web>
Опция cookieless включает возможные установки, перечисленные в таблице ниже:
Опция | Описание |
---|---|
UseCookies | Принуждает исполняющую среду использовать cookie-наборы при аутентификации с помощью форм. Это требует от клиентского браузера поддержки cookie-наборов. Если браузер не поддерживает cookie-наборы, аутентификация с помощью форм просто не работает с активизированной этой установкой. Поскольку среда ASP.NET не получает действительного cookie-набора аутентификации от браузера, она вновь и вновь перенаправляет клиента на страницу входа, результатом чего будет бесконечный цикл |
UseUri | Если выбрана эта опция конфигурации, cookie-наборы для аутентификации не используются. Вместо этого исполняющая среда кодирует билет аутентификации с помощью форм в запрошенный URL-адрес, и инфраструктура обрабатывает эту специфическую часть URL-адрес для установки контекста безопасности |
AutoDetect | Инициирует применение cookie-наборов, если клиентский браузер их поддерживает. В противном случае используется URL-кодировка билета. Устанавливается через механизм зондирования |
UseDeviceProfile | Решение о применении cookie-наборов или URL-кодировки принимается на основе конфигурации профиля, хранящейся на сервере. Профили хранятся в файлах .browser в каталоге C:\[Каталог Windows]\Microsoft.NET\Framework\[Версия]\CONFIG\Browsers |
Специальное хранилище удостоверений
Как уже упоминалось ранее, хранилище удостоверений в web.config подходит только для простейших сценариев. Отказаться от использования web.config в качестве хранилища удостоверений можно по нескольким причинам:
Потенциальная нехватка защищенности. Несмотря на то что пользователи не имеют возможности напрямую запрашивать файл web.config, предпочтение все-таки может быть отдано хранилищу с более эффективной защитой. До тех пор, пока эта информация хранится на веб-сервере, пароли доступны любому администратору, разработчику или специалисту по тестированию, имеющему к нему доступ.
Отсутствие поддержки добавления специфичной для пользователя информации. Например, может понадобиться сохранять такую информацию, как адреса, номера кредитных карт, персональные предпочтения и т.п.
Низкая производительность при большом количестве пользователей, web.config - это всего лишь файл, и он не поддерживает эффективное кэширование и многопользовательский доступ, присущий базам данных. Более того, после каждого изменения файла web.config перезапускается HttpApplication, что приводит к утере всех доменов приложений, состояний сеанса и т.д. Восстановление всего этого влияет на производительность.
Таким образом, в большинстве приложений вы будете применять собственные хранилища имен и паролей пользователей, и большей частью они будут находиться в базах данных вроде SQL Server.
В ASP.NET 1.x приходилось реализовывать такие сценарии самостоятельно. Внутри формы входа необходимо было подключаться к базе данных, проверять наличие пользователя, сравнивать введенный пароль с хранящимся в базе и затем вызывать FormsAuthentication.RedirectFromLoginPage, если имя пользователя и пароль были указаны корректно. К счастью, ASP.NET предлагает готовую к использованию инфраструктуру, а также полный набор элементов управления, связанных с безопасностью. Платформа Membership API включает хранилище данных на основе SQL Server, в которое записываются пользователи и роли, а также функции для проверки имен и паролей, без необходимости знания подробностей устройства лежащей в основе базы данных. Мы рассмотрим этот интерфейс в следующих статьях.
Постоянные cookie-наборы в аутентификации с помощью форм
В продемонстрированных до сих пор примерах поддержка билетов аутентификации между запросами осуществлялась с помощью непостоянных cookie-наборов аутентификации. Непостоянство означает, что после закрытия браузера пользователем такие cookie-наборы немедленно удалялись. Это разумный шаг для обеспечения безопасности. И особенно он важен на компьютерах общего пользования, поскольку он предотвращает использование следующим пользователем билета, выданного предыдущему. Непостоянные cookie-наборы затрудняют и ограничивают возможности атак перехватом сеанса (когда злоумышленник получает доступ к сети и похищает cookie-набор другого пользователя).
Но, несмотря на повышенный риск нарушения безопасности при использовании постоянных cookie-наборов аутентификации, в некоторых ситуациях их применение все же оправдано. Если выполняется аутентификация для целей персонализации, а не управления доступом к ограниченным ресурсам, то можно воспользоваться преимуществами исключения регистрации при каждом визите пользователя, даже невзирая на повышенную опасность неавторизованного доступа.
Если вы решили применять постоянные cookie-наборы, реализовать их достаточно просто. Для этого понадобится передать значение true вместо false во втором параметре метода RedirectFromLoginPage() или SetAuthCookie() класса FormsAuthentication. Вот пример:
FormsAuthentication.RedirectFromLoginPage(UsernameTextBox.Text, true);
Постоянные cookie-наборы не устаревают по закрытии браузера. Подобно непостоянным cookie-наборам, они устаревают, когда вызывается метод FormsAuthentication.SignOut(), или же когда достигнут лимит времени, установленного в атрибуте timeout элемента <forms> (по умолчанию 30 минут). Это порождает потенциальную проблему. В некоторых приложениях пользователи должны иметь выбор - применять кратковременный непостоянный cookie-набор или же хранить долгосрочный постоянный. Тем не менее, для атрибута timeout допускается указывать только одно значение. Решение состоит в использовании метода GetAuthCookie() класса FormsAuthentication для создания постоянного cookie-набора, установки вручную даты и времени, когда cookie-набор должен стать недействительным, и самостоятельном включении постоянного cookie-набора в ответ HTTP.
В следующем примере переписан код аутентификации пользователя при щелчке на кнопке входа. Он создает постоянный cookie-набор, но выполняет также дополнительные шаги, чтобы ограничить время его действия 10 днями:
protected void LoginAction_Click(object sender, EventArgs e)
{
Page.Validate();
if (!Page.IsValid) return;
if (FormsAuthentication.Authenticate(UsernameText.Text, PasswordText.Text))
{
// Создать cookie-набор аутентификации
HttpCookie AuthCookie = FormsAuthentication.GetAuthCookie(UsernameText.Text, true);
AuthCookie.Expires = DateTime.Now.AddDays(10);
// Добавить cookie-набор в ответ
Response.Cookies.Add(AuthCookie);
// Перенаправить на исходную запрошенную страницу
Response.Redirect(FormsAuthentication.GetRedirectUrl(UsernameText.Text, true));
}
else
{
// Имя и пароль пользователя неправильны
LegendStatus.Text = "Вы неправильно ввели имя пользователя или пароль!";
}
}
Код проверки удостоверения в этом сценарии прежний. Единственное отличие состоит в том, что cookie-набор аутентификации не добавляется автоматически. Вместо этого он создается вызовом GetAuthCookie(), который возвращает новый экземпляр HttpCookie. После того, как cookie-набор аутентификации создан, можно получить текущее время и дату (с помощью статического свойства DateTime.Now), добавить к нему 10 дней (методом DateTime.AddDays()) и установить полученное значение в качестве даты устаревания данного cookie-набора. Далее cookie-набор добавляется в HTTP-ответ и, наконец, можно перенаправить пользователя на исходный запрошенный URL-адрес, который получается с помощью метода GetRedirectUrl().
В результате получаем cookie-набор, который сохраняется при закрытии браузера, но время действия которого истекает через 10 дней, когда пользователю придется ввести заново свое удостоверение, чтобы снова аутентифицироваться на веб-сайте.