Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 35:

Управление состоянием страниц на клиенте

Упражнение 4. Шифрование информации в состоянии вида

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

  • Добавьте к проекту новую страницу (без файла отделенного кода) с именем DisplayViewState.aspx и установите ее стартовой
  • Удалите из интерфейсной части страницы дескриптор <div></div>
  • Включите трассировку, добавив в директиву @ Page страницы параметр Trace = true
  • Добавьте в блок скриптов кодовую часть, чтобы окончательно страница стала такой
<%@ Page Language="C#" Trace="true" %>
    
<script runat="server">
    
    protected void Page_Load(object sender, EventArgs e)
    {
        // Объекты создаем каждый раз заново
        Label label = new Label();
        this.form1.Controls.Add(label);
        
        this.form1.Controls.Add(new HtmlGenericControl("br"));
        
        Button button = new Button();
        this.form1.Controls.Add(button);
        
        if (!this.IsPostBack)
        {
            // Один раз настроили, а дальше работает состояние вида
            label.Text = "Привет всем!!!";
            label.ForeColor = System.Drawing.Color.Red;
            label.Font.Bold = true;
                
            button.Text = "Отправить";
        }
    }
    
    protected void Page_PreRender(object sender, EventArgs e)
    {
        // Вывод на страницу строки состояния
        if (this.IsPostBack)
        {
            // Извлекаем строку состояния
            string strViewState = this.Request.Form["__VIEWSTATE"];
    
            // Добавляем вывод к родительской форме 
            Label label = new Label();
            this.form1.Controls.Add(label);
            label.Text = "<p><b>Строка состояния как есть:</b><br />" 
                + strViewState + "</p>";
            label.EnableViewState = false;// Чтобы не влияла на результат
    
            // Вставляем в отклик напрямую 
            Response.Write("<b>Строка состояния в формате ASCII:</b><br />");
            
            // Преобразуем строку из Base64 в массив ASCII-символов 
            byte[] ascii = Convert.FromBase64String(strViewState);
            Response.BinaryWrite(ascii);
    
            // То же самое, только по турецки...
            // Десериализация и отображение строки
            //string decodedViewState = System.Text.Encoding.ASCII.GetString(ascii);
            //Response.Write(decodedViewState);
        }
    }
</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>
Листинг 35.12. Окончательный код страницы DisplayViewState.aspx
  • Запустите страницу DisplayViewState.aspx и повыполните обратную отсылку

HTML-отклик на стороне клиента, сгенерированный страницей DisplayViewState.aspx после обратной отсылки, будет выглядеть примерно так

Мы видим, что некоторая информация, сохраняемая на клиенте в строке состояния вида, может быть частично расшифрована. Злоумышленник может извлечь из нее нужные для себя сведения.

Для большей защиты строки состояния вида можно использовать встроенное шифрование, которое включается на странице параметром ViewStateEncryptionMode = "Always" директивы @ Page или установкой в конфигурационном файле секции

<pages viewStateEncryptionMode="Always">

  • Включите шифрование добавлением в директиву @Page атрибута ViewStateEncryptionMode = "Always" и выполните страницу DisplayViewState.aspx

Сгенерированный отклик на обратную отсылку станет примерно таким


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

Для предотвращения того, что злоумышленник может изменить строку состояния нужным для себя образом и попытаться отправить ее на сервер, предусмотрена встроенная защита в виде подсчета контрольной суммы. Поскольку состояние вида формирует только серверная сторона, то перед включением строки в скрытое поле HTML-вывода ASP.NET вычисляет контрольную сумму и прячет ее в ту же самую строку. При получении этой хэшированной строки от клиента система десериализует ее, вновь вычисляет контрольную сумму и сравнивает с ранее спрятанной. При обнаружении расхождений система генерирует ошибку. Подсчет контрольной суммы включается в директиве @ Page параметром EnableViewStateMac = "true".

Упражнение 5. Скрытые поля

Во вкладке Standard панели Toolbox имеется элемент управления HiddenField, который генерирует скрытое поле c единственной переменной, определяемой его свойством Value. Этот элемент не поддерживает шифрование, хэширование и хранение по частям. Поэтому клиенты в исходном коде HTML-вывода могут просматривать эти данные. Данные в скрытом поле хранятся до тех пор, пока пользователь не перейдет к другой странице.

  • Добавьте к проекту новую страницу с именем HiddenFieldTest.aspx без файла отделенного кода и назначьте ее стартовой
  • Переведите страницу в режим Design и двойным щелчком на ее свободном месте создайте в скриптах обработчик Page_Load()
  • Поместите на Web-форму из вкладки Standard панели Toolbox два элемента управления HiddenField и присвойте им имена beginTime и countRequest
  • Отключите для всех элементов страницы механизм сохранения состояния вида, добавив в директиву @ Page параметр EnableViewState = "false"
  • Заполните страницу следующим кодом
<%@ Page Language="C#" EnableViewState="false" %>
    
<script runat="server">
    
    protected void Page_Load(object sender, EventArgs e)
    {
        // Создаем текстовую метку
        lblTime = new Label();
        div1.Controls.Add(lblTime);
        div1.Controls.Add(new HtmlGenericControl("br"));
    
        // Создаем кнопку CurrentTime
        Button button = new Button();
        div1.Controls.Add(button);
        // Подписываемся на событие кнопки
        button.Click += CurrentTimeClick;
        button.Text = "Текущее время";
        
        // Добавляем промежуток между кнопками
        Literal literal = new Literal();
        literal.Text = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
        div1.Controls.Add(literal);
    
        // Создаем кнопку OldTime
        button = new Button();
        div1.Controls.Add(button);
        // Подписываемся на событие кнопки
        button.Click += OldTimeClick;
        button.Text = "Начальное время";
    
        // Инициализируем скрытые поля и метку при первом запросе
        if (!this.IsPostBack)
        {
            beginTime.Value = System.DateTime.Now.ToLongTimeString();
            OldTimeClick(null, EventArgs.Empty);
        }
        else
        {
            // Ведение счетчика запросов страницы 
            int.TryParse(countRequest.Value, out count);
            count++;
            // Или то же самое
            // count = int.Parse(countRequest.Value) + 1;
        }
        
        countRequest.Value = count.ToString();
    }
    
    // Вынесены в поля класса для видимости в обработчиках
    int count = 0;
    Label lblTime;
    
    void CurrentTimeClick(object sender, EventArgs e)
    {
        lblTime.Text = "Текущее время: " + System.DateTime.Now.ToLongTimeString();
        lblTime.Text += "<br />Отклик № " + count.ToString();
    }
    
    void OldTimeClick(object sender, EventArgs e)
    {
        lblTime.Text = "Начальное время: " + beginTime.Value;
        lblTime.Text += "<br />Отклик № " + count.ToString();
    }
</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" id="div1" runat="server">
            <h1 style="color: Red">
                Испытание элемента HiddenField
            </h1>
            <asp:HiddenField ID="beginTime" runat="server" />
            <asp:HiddenField ID="countRequest" runat="server" />
        </div>
    </form>
</body>
</html>
Листинг 35.13. Код страницы HiddenFieldTest.aspx
  • Исполните страницу и убедитесь, что скрытые поля, генерируемые элементом HiddenField, в пределах одной страницы сохраняют информацию
Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ