Безопасность: аутентификация с помощью форм
Упражнение 2. Хеширование паролей в конфигурационном файле
В нашем файле Web.config до настоящего момента пароли хранились в виде открытого текста. В таком виде они легко уязвимы со стороны продвинутых пользователей, а также со стороны административного персонала сервера. Их было бы желательно защитить. На этот случай предусмотрена возможность хеширования, которая заключается в предварительной обработке исходного текста некоторой функцией, выход которой невозможно восстановить в обратную сторону.
В секции <credentials> имеется параметр passwordFormat. Он предназначен для указания ASP.NET, в каком формате хранятся значения паролей и какой алгоритм хеширования к ним применялся. Если пароли предварительно хешированы и сохранены в таком виде в конфигурационном файле, то ASP.NET при получении пароля от пользователя также его хеширует, прежде чем сравнить с храняшимся на сервере.
Параметр passwordFormat может принимать одно из трех следующих значений:
- Clear - пароль не хеширован и пользовательский пароль нужно сравнивать без предварительного хеширования
- MD5 - пароль хеширован алгоритмом MD5 и перед сравнением присланный пользовательзователем пароль хешировать тем же алгоритмом
- SHA1 - для хеширования применять алгоритм SHA1
Для предварительного хеширования паролей в Web.config применяется метод FormsAuthentication.HashPasswordForStoringInConfigFile().
- Добавьте к проекту страницу HashPasswordPage.aspx с встроенным скриптом и наполните ее следующим кодом
<%@ Page Language="C#" %> <%@ Import Namespace="System.Web.Configuration" %> <script runat="server"> protected void btnHashedPwd_Click(object sender, EventArgs e) { // Загрузить содержимое корневого Web.config Configuration myConfig = System.Web.Configuration. WebConfigurationManager.OpenWebConfiguration("~/"); // Найти содержимое секции system.web ConfigurationSectionGroup systemWeb = myConfig.SectionGroups["system.web"]; // Найти содержимое секции authentication AuthenticationSection authSec = (AuthenticationSection) systemWeb.Sections["authentication"]; // Установить параметр passwordFormat, сообщающий, что было хеширование по MD5 authSec.Forms.Credentials.PasswordFormat = FormsAuthPasswordFormat.MD5; // Извлекаем из коллекции Users старые имена и пароли int count = authSec.Forms.Credentials.Users.Count; string[] name = new string[count]; string[] clearTextPwd = new string[count]; int i = 0; foreach (FormsAuthenticationUser user in authSec.Forms.Credentials.Users) { name[i] = user.Name; clearTextPwd[i] = user.Password; i++; } // Очищаем коллекцию Users authSec.Forms.Credentials.Users.Clear(); // Добавляем старые имена и новые хешированные пароли for (i = 0; i < count; i++) { // Хешируем исходный пароль string hashedPwd = FormsAuthentication. HashPasswordForStoringInConfigFile(clearTextPwd[i], "MD5"); // Добавляем в коллекцию Users authSec.Forms.Credentials.Users.Add( new FormsAuthenticationUser(name[i], hashedPwd)); } // Обновляем файл Web.config myConfig.Save(); // Отсылаем сообщение lblHashResult.Text = "Хеширование завершено.<br />" + "Повторно не выполнять - будет хеш на хеш!!!"; } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div style="text-align: center"> <h1> Административная страница хеширования паролей в Web.config</h1> <p> <asp:Button ID="btnHashedPwd" runat="server" OnClick="btnHashedPwd_Click" Text="Хешировать" /></p> <p> <asp:Label ID="lblHashResult" runat="server"></asp:Label></p> </div> </form> </body> </html>Листинг 37.10. Код страницы HashPasswordPage.aspx хеширования паролей в Web.config
- Выполните страницу HashPasswordPage.aspx, но только один раз, чтобы повторно не хешировать уже хешированные пароли
В результате мы получим вариант конфигурационного файла с хешированными паролями
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <compilation debug="true" /> <authentication mode="Forms"> <forms name="MyCookieName" loginUrl="MyLogin.aspx" defaultUrl="MyDefault.aspx" protection="All" timeout="20" path="/" requireSSL="false" slidingExpiration="true" cookieless="AutoDetect" domain="" enableCrossAppRedirects="false" > <credentials passwordFormat="MD5"> <user name="admin" password="FA03EB688AD8AA1DB593D33DABD89BAD" /> <user name="петя" password="B63974E3EE7B0920FE24DFB8E82FAE81" /> <user name="вася" password="44D4B2D42E830BE0DD43E6830D957C5D" /> <user name="user1" password="7C6A180B36896A0A8C02787EEAFB0E4C" /> <user name="user2" password="6CB75F652A9B52798EB6CF2201057C73" /> <user name="user3" password="819B0643D6B89DC9B579FDFC9094F28E" /> </credentials> </forms> </authentication> <authorization> <deny users="?"/> </authorization> </system.web> </configuration>Листинг 37.11. Файл Web.config после выполнения процедуры хеширования паролей
- Откройте в редакторе оболочки страницу Default.aspx и командой File/View in Browser запустите ее на выполнение
Вначале будет предложена страница регистрации, поскольку конфигурация сайта настроена именно так.
Исходные пароли, вводимые пользователями при регистрации, остались прежними. Но в конфигурационном файле они теперь храняться как хешированные по алгоритму MD5 и являются бесполезными для злоумышленников. Сама же система при аутентификации приводит исходные пароли перед сравнением к хешированному виду по тому же самому алгоритму. Если исходный и оригинальный пароли правильные, то их хеши будут совпадать.
Здесь есть одно слабое звено: при регистрации учетная запись по нешифрованному каналу связи может быть перехвачена злоумышленником. В этом случае хранение удостоверений в хешированном виде становится бесполезным.
Естественно, что подобная страница хеширования паролей относится к системе администрирования и не должна быть доступна обычным пользователям. Ею может пользоваться только обслуживающий персонал сайта.
Упражнение 3. Закрепление за броузером постоянной аутентификации форм
До сих пор мы использовали временные cookie-наборы аутентификации для конкретного пользователя, которые действуют только на период сеанса и сразу удаляются броузером после его завершения. Это обеспечивает разумную безопасность. Но иногда может потребоваться закрепить аутентификацию не за пользователем, а за броузером. В этом случае при первой регистрации пользователя можно разрешить ему создать на броузере постоянный cookie-набор, по которому бы узнавался сам броузер.
Постоянные cookie-набор можно создать
- в странице регистрации при первой посещении
пользователя установкой второго параметра в значение
true (вместо false) в методе
FormsAuthentication.RedirectFromLoginPage( строка_имя , true);
- в любом другом месте открытого сеанса выполнением метода
FormsAuthentication.SetAuthCookie( строка_имя , true);
- в любом другом месте открытого сеанса выполнением кода
HttpCookie authCookie = FormsAuthentication.GetAuthCookie( строка_имя , true);
Установленный таким способом cookie-набор будет существовать на компьютере пользователя "вечно", если только мы не удалим его методом
FormsAuthentication.SignOut();
Можно также изменить временной статус постоянного cookie-набора на любой странице. Покажем это
- Добавьте к проекту страницу ModifyPersistentCookieAuth.aspx без файла отделенного кода и заполните ее так
<%@ Page Language="C#" EnableViewState="false" %> <script runat="server"> Label message; protected void Page_Load(object sender, EventArgs e) { // Дескриптор центрирования HtmlGenericControl center = new HtmlGenericControl("center"); form1.Controls.Add(center); // Текстовая метка с заголовком Label label = new Label(); center.Controls.Add(label); label.Text = "<h2>Закрепить аутентификацию за броузером</h2>"; // Кнопки Button createPersistentCookie = new Button(); center.Controls.Add(createPersistentCookie); createPersistentCookie.Text = "Создать бессрочный AuthCookie"; createPersistentCookie.Click += new EventHandler(createPersistentCookie_Click); center.Controls.Add(new HtmlGenericControl("br")); Button createTemporaryCookie = new Button(); center.Controls.Add(createTemporaryCookie); createTemporaryCookie.Text = "Создать срочный AuthCookie"; createTemporaryCookie.Click += new EventHandler(createTemporaryCookie_Click); center.Controls.Add(new HtmlGenericControl("br")); Button deleteCookie = new Button(); center.Controls.Add(deleteCookie); deleteCookie.Text = "Удалить AuthCookie"; deleteCookie.Click += new EventHandler(deleteCookie_Click); center.Controls.Add(new HtmlGenericControl("br")); // Текстовая метка с сообщением message = new Label(); center.Controls.Add(message); message.Text = String.Empty; } void createPersistentCookie_Click(object sender, EventArgs e) { // Создать бессрочный cookie HttpCookie authCookie = FormsAuthentication.GetAuthCookie("xx", true); // Отослать на броузер this.Response.Cookies.Add(authCookie); // Сообщить пользователю message.Text = "Бессрочный AuthCookie создан!"; } void createTemporaryCookie_Click(object sender, EventArgs e) { // Создать вначале бессрочный cookie HttpCookie authCookie = FormsAuthentication.GetAuthCookie("xx", true); // Настроить его как временный сроком на 10 дней authCookie.Expires = DateTime.Now.AddDays(10); // Отослать на броузер this.Response.Cookies.Add(authCookie); // Сообщить пользователю message.Text = "Срочный AuthCookie создан!"; } void deleteCookie_Click(object sender, EventArgs e) { // Удалить cookie-набор регистрации FormsAuthentication.SignOut(); // Сообщить пользователю message.Text = "AuthCookie удален!"; } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> </form> </body> </html>Листинг 37.12. Страница создания на броузере постоянного cookie-набора аутентификации
Интерфейс страницы в режиме выполнения будет таким
При закреплении cookie-набора за броузером при соединении с сайтом страница регистрации пользователю предъявляться не будет, пока не закончится срок действия (если cookie-набор срочный).
API Membership является готовым механизмом в ASP.NET, предназначенным для организации управления аутентификацией пользователей. Он базируется на аутентификации форм. Единственное отличие, что хранение пользователей будет осуществляться не в конфигурационном файле, а в базе данных. И для управления задействуются классы Membership.
Чаще всего к корневому каталогу сайта допускаются все анонимные пользователи без регистрации, а защищенные ресурсы, требующие аутентификацию, размещаются в подкаталогах со своими файлами Web.config. Если кто-то запрашивает защищенный ресурс из подкаталога, система автоматически перенаправляет пользователя на страницу регистрации.
Для использования API Membership нужно выполнитиь следующие шаги: