Опубликован: 07.05.2010 | Доступ: свободный | Студентов: 1678 / 62 | Оценка: 4.56 / 4.06 | Длительность: 34:11:00
Лекция 18:

Безопасность

< Лекция 17 || Лекция 18: 123456789

Создание страницы регистрации

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

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

  • Создайте новую страницу с именем MyLogin.aspx без отделенного кода
  • Заполните страницу элементами управления и настройте их согласно таблице
Таблица свойств интерфейсной части страницы регистрации MyLogin.aspx
Элемент Свойство Значение Пояснение
Panel ID MainPanel Родительская панель для оформления рамкой
BorderColor Silver Цвет рамки
BorderStyle Ridge Тип рамки
TextBox ID UsernameText Текстовое поле ввода имени пользователя
RequiredFieldValidator ID UsernameRequiredValidator Проверка достоверности, чтобы привязанное поле не было пустым
ControlToValidate UsernameText Привязка к контролируемому элементу ввода
Display Dynamic Место под элемент выделять только при визуализации
ErrorMessage Не заполнено поле "Имя пользователя" Отображать в элементе ValidationSummary при ошибке
ToolTip Пустое поле ввода Всплывающая подсказка для визуализированного валидатора
Text * Надпись на визуализированном валидаторе
RegularExpressionValidator ID UsernameValidator Валидатор регулярных выражений. Следит за вводом только допустимых символов
ControlToValidate UsernameText Привязка к контролируемому элементу ввода
Display Dynamic Место под элемент выделять только при визуализации
ErrorMessage Неверное имя пользователя Отображать в элементе ValidationSummary при ошибке
ToolTip Допустимы буквы, цифры, пробелы и подчеркивания Всплывающая подсказка для визуализированного валидатора
ValidationExpression [а-яА-Я\w| ]* Регулярное выражение:
  • [] - диапазон пропускаемых символов
  • []* - символы диапазона могут встречаться в контролируемом вводе сколько угодно раз
  • а-яА-Я - пропускать все буквы русского алфавита
  • \w - пропускать все латинские буквы, цифры и знаки подчеркивания. Это управляющий символ регулярного выражения
  • | - знак ИЛИ, после которого стоит пробел, означает, что пропускать и пробелы
Таким образом: действительными считаются все русские буквы обоих регистров, все латинские буквы обоих регистров, цифры и знаки подчеркивания, а также пробелы. И все это в любом количестве повторений
Text * Надпись на визуализированном валидаторе
TextBox ID PasswordText Текстовое поле ввода пароля
TextMode Password Ввод отображается звездочками
RequiredFieldValidator ID PwdRequiredValidator Проверка достоверности, чтобы привязанное поле не было пустым
ControlToValidate PasswordText Привязка к контролируемому элементу ввода
Display Dynamic Место под элемент выделять только при визуализации
ErrorMessage Не заполнено поле "Пароль" Отображать в элементе ValidationSummary при ошибке
ToolTip Пустое поле ввода Всплывающая подсказка для визуализированного валидатора
Text * Надпись на визуализированном валидаторе
RegularExpressionValidator ID PwdValidator Валидатор регулярных выражений. Следит за вводом только допустимых символов
ControlToValidate PasswordText Привязка к контролируемому элементу ввода
Display Dynamic Место под элемент выделять только при визуализации
ErrorMessage Неверный пароль Отображать в элементе ValidationSummary при ошибке
ToolTip Используются недопустимые символы Всплывающая подсказка для визуализированного валидатора
ValidationExpression [а-яА-Я\w|!"$&amp;/()=\-?\*]* Регулярное выражение:
  • [] - диапазон пропускаемых символов
  • []* - символы диапазона могут встречаться в контролируемом вводе сколько угодно раз
  • а-яА-Я - пропускать все буквы русского алфавита
  • \w - пропускать все латинские буквы, цифры и знаки подчеркивания. Это управляющий символ регулярного выражения
  • | - знак ИЛИ, после которого стоит пробел и ряд других символов, разрешенных для ввода
Символы, которые могут интерпретироваться как служебные, экранируются обратной косой чертой. Интерпретируемый HTML-символ амперсанта заменяется неинтерпретируемым аналогом &amp;
Text * Надпись на визуализированном валидаторе
Button ID Button1 Кнопка Submit
Text Отправить Надпись
Label ID lblResult Метка сообщения для заполнения в коде
ValidationSummary ID ValidationSummary1 Элемент отображения сообщений об ошибках, обнаруженных валидаторами

Интерфейсная часть страницы MyLogin.aspx в режиме проектирования будет такой


Код страницы MyLogin.aspx будет таким

<%@ Page Language="C#" EnableViewState="false" %>
    
<script runat="server">
    
    protected void LoginAction_Click(object sender, EventArgs e)
    {
        this.Validate();// Исполнить валидаторы на сервере
        if (!this.IsValid)// Оценить флаг достоверности
            return;// Отправить назад как есть
        
        // Извлечь из web.config и сравнить с введенным 
        if (FormsAuthentication.Authenticate(UsernameText.Text, PasswordText.Text))
        {
            // Создать временный cookie-набор (второй параметр false),
            // записать в него  метку аутентификации и перенаправить
            // на исходную запрошенную страницу или MyDefault.aspx
            FormsAuthentication.RedirectFromLoginPage(UsernameText.Text, false);
        }
        else
        {
            HtmlGenericControl message = new HtmlGenericControl();
            message.InnerHtml = "<h2 style='color: Red'>Неверное имя или пароль!</h2>";
            form1.Controls.Add(message);
        }
    }
</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">
            <h2>
                Введите свои имя и пароль</h2>
            <asp:Panel ID="MainPanel" runat="server" BorderColor="Silver" 
                BorderStyle="Ridge"
                BorderWidth="2px" Height="90px" Width="412px">
                <table border="0" cellpadding="5" cellspacing="0" style="width: 100%">
                    <tr>
                        <td>
                            &nbsp;&nbsp;&nbsp;
                        </td>
                        <td align="right" height="43" style="width: 167px">
                            Имя&nbsp;пользователя:</td>
                        <td>
                            &nbsp;<asp:TextBox ID="UsernameText" runat="server" />
                        </td>
                        <td>
                            <asp:RequiredFieldValidator ID="UsernameRequiredValidator" 
                                runat="server" ControlToValidate="UsernameText"
                                Display="Dynamic" 
                                ErrorMessage='Не заполнено поле "Имя пользователя"' 
                                ToolTip="Пустое поле ввода">*
                            </asp:RequiredFieldValidator>
                            <asp:RegularExpressionValidator ID="UsernameValidator" 
                                runat="server" ControlToValidate="UsernameText"
                                Display="Dynamic" ErrorMessage="Неверное имя пользователя" 
                                ToolTip="Допустимы буквы, цифры, пробелы и подчеркивания"
                                ValidationExpression="[а-яА-Я\w| ]*">*
                            </asp:RegularExpressionValidator>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            &nbsp;&nbsp;&nbsp;
                        </td>
                        <td align="right" height="43" style="width: 167px">
                            &nbsp;Пароль:</td>
                        <td>
                            &nbsp;<asp:TextBox ID="PasswordText" runat="server" 
                                TextMode="Password" />
                        </td>
                        <td>
                            <asp:RequiredFieldValidator ID="PwdRequiredValidator" 
                                runat="server" ControlToValidate="PasswordText"
                                Display="Dynamic" ErrorMessage='Не заполнено поле "Пароль"' 
                                ToolTip="Пустое поле ввода">*
                            </asp:RequiredFieldValidator>
                            <asp:RegularExpressionValidator ID="PwdValidator" runat="server" 
                                ControlToValidate="PasswordText"
                                Display="Dynamic" ErrorMessage="Неверный пароль" 
                                ToolTip="Используются недопустимые символы"
                                ValidationExpression='[а-яА-Я\w| !"$&amp;/()=\-?\*]*'>*
                            </asp:RegularExpressionValidator>
                        </td>
                    </tr>
                </table>
                <asp:Button ID="Button1" runat="server" Text="Отправить" 
                    OnClick="LoginAction_Click" />
            </asp:Panel>
            <asp:Label ID="lblResult" runat="server" />
        </div>
        <asp:ValidationSummary ID="ValidationSummary1" runat="server" />
    </form>
</body>
</html>

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

Хеширование паролей в Web.config

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

В секции <credentials> имеется параметр passwordFormat. Он предназначен для указания ASP.NET, в каком формате хранятся значения паролей и какой алгоритм хеширования к ним применялся. Если пароли предварительно хешированы и сохранены в таком виде в конфигурационном файле, то ASP.NET при получении пароля от пользователя также его хеширует, прежде чем сравнить с храняшимся на сервере.

Параметр passwordFormat может принимать одно из трех следующих значений:

  • Clear - пароль не хеширован и пользовательский пароль нужно сравнивать без предварительного хеширования
  • MD5 - пароль хеширован алгоритмом MD5 и перед сравнением присланный пользовательзователем пароль хешировать тем же алгоритмом
  • SHA1 - для хеширования применять алгоритм SHA1

Для предварительного хеширования паролей в Web.config применяется следующий метод

string hashedPwd = FormsAuthentication.HashPasswordForStoringInConfigFile(clearTextPassword, "MD5");
  • Добавьте к проекту страницу HashPasswordPage.aspx и наполните ее следующим кодом
    <%@ Page Language="C#" %>
        
    <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 
            System.Web.Configuration.AuthenticationSection authSec =
                (System.Web.Configuration.AuthenticationSection)
                systemWeb.Sections["authentication"];
            
            // Установить параметр passwordFormat, сообщающий, что было хеширование по MD5
            authSec.Forms.Credentials.PasswordFormat = System.Web.Configuration.FormsAuthPasswordFormat.MD5;
            
            // Извлекаем из коллекции Users старые имена и пароли 
            int count = authSec.Forms.Credentials.Users.Count;
            string[] name = new string[count];
            string[] clearTextPwd = new string[count];
            int i = 0;
            foreach (System.Web.Configuration.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 System.Web.Configuration.FormsAuthenticationUser(name[i], hashedPwd));
            }
            
            // Обновляем файл Web.config 
            myConfig.Save();
            
            // Отсылаем сообщение 
            lblHashResult.Text = "Хеширование завершено.<br />"
                + "Повторно не выполнять - будет хеш на хеш!!!";
        }
    </script>
        
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head 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>
  • Выполните страницу 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>

Исходные пароли, вводимые пользователями при регистрации, остались прежними. Но в конфигурационном файле они теперь храняться как хешированные по алгоритму MD5 и являются бесполезными для злоумышленников. Зама же система при аутентификации приводит исходные пароли перед сравнением к хешированному виду по тому же самому алгоритму. Если исходный и оригинальный пароли правильные, то их хеши будут совпадать.

< Лекция 17 || Лекция 18: 123456789