научный журнал «Актуальные исследования» #10 (13), май '20

Применение аспектно-ориентированного программирования для шифрования строк HTTP-запроса в ASP.NET-приложении

В статье описывается и анализируется задача шифрования строк HTTP-запроса (Query Strings) в ASP.NET-приложении. Предлагается метод применения аспектно-ориентированного программирования для решения описанной задачи. Разрабатывается аспект шифрования строк HTTP-запроса в системе Aspect.NET.

Аннотация статьи
Aspect.NET
ASP.NET-приложение
строка HTTP-запроса
АОП
аспектно-ориентированное программирование
Ключевые слова

1. Введение

Один из способов передачи информации между страницами в Веб-приложениях – через строки HTTP-запроса (Query Strings). Строка HTTP-запроса есть часть унифицированного указателя ресурса (URL), который содержит данные для передачи в Веб-приложения [1]. Например, если URL-адрес запроса:

http://localhost/Recipient.aspx?mydata=asp.net&mytime=27-May-09+14%3a38%3a10

тогда значение строки запроса равно

mydata=asp.net&mytime=27-May-09+14%3a38%3a10

Во многих ситуациях информация URL-запроса, соответствующая поданным пользователем данным, не имеет проблему того, что пользователь может видеть или модифицировать ее. Но в других ситуациях строка запроса содержит информацию, которую пользователям нельзя увидеть. В данных ситуациях, для решения этой проблемы либо используются другие методы передачи информации между страницами (state management, например View state, Session, Cookies – они могут имеют другие ограничения), либо шифруется строка запроса.

В данной статье рассмотрим, как шифровать строку запроса в ASP.NET-приложении с помощью аспектно-ориентированного программирования (АОП) в системе Aspect.NET [2], разработанной в лаборатории Java-технологии математико-механического факультета СПбГУ под руководством профессора В.О. Сафонова.

2. Шифрование строк запроса (Query String)

В [3] авторы демонстрируют интересный пример шифрования строки запроса. Будем использовать метод шестнадцатеричного шифрования (Hex Encoding) этих авторов для шифрования строки запроса.

//Hexadecimal-based encoding, which replaces each character with alphanumeric code.

protected static string GetString(byte[] data)

{

     StringBuilder sb = new StringBuilder();

     foreach (byte b in data)

     {

          sb.Append(b.ToString("X2"));

     }

     return sb.ToString();

}

//Decode hexadecimal-based string

protected static byte[] GetBytes(string data)

{

     //GetString encodes the hex numbers with two digits

     byte[] Results = new byte[data.Length / 2];

     for (int i = 0; i < data.Length; i += 2)

     {

          Results[i / 2] = Convert.ToByte(data.Substring(i, 2), 16);

     }

     return Results;

}

Метод GetString() преобразует массив данных типа byte в шестнадцатеричную строку, а метод GetBytes() выполняет обратное действие, т.е. преобразует шестнадцатеричную строку в массив данных.

Создаем пример шифрования строки запроса. Рассмотрим следующий код:

protected void SendCommand_Click(object sender, EventArgs e)

{

     string ClearQueryString = "mydata=" + HttpUtility.UrlEncode(MyData.Text) + "&mytime=" + HttpUtility.UrlEncode(DateTime.Now.ToString());

     byte[] EncryptedData = SymEncryption.Encrypt(ClearQueryString);

     string NewQuery = "Recipient.aspx?data=" + GetString(EncryptedData);

     Response.Redirect(NewQuery);

}

В этом примере явная строка запроса ClearQueryString шифруется симметричным алгоритмом шифрования (Symmetric encryption: SymEncryption). Можно посмотреть как использовать криптографию в .NET в [3]. Здесь, кроме симметричного алгоритма можно использовать любые методы шифрования, например:

byte[] EncryptedData = ProtectedData.Protect(Encoding.UTF8.GetBytes(ClearQueryString),

     null, DataProtectionScope.LocalMachine);

Здесь используется Windows data protection API (DPAPI) для шифрования (см. [3, 4]).

После шифрования строки запроса, необходимо преобразовать зашифрованные данные EncryptedData в строку (string) для подачи в URL-запрос. Один из подходов – использование статического метода Convert.ToBase64String(), который создает Base64-закодированную строку. Но Base64-строка содержит символы, не допустимые в строке запроса. Поэтому зашифрованные данные преобразуются в шестнадцатеричную строку с помощью метода GetString(). После этого запрос перенаправляется вызовом метода HttpResponse.Redirect(). И наш URL-запрос будет иметь такой вид:

http://localhost/Recipient.aspx?data=688558E956A2970ECEBB7BA7F7228331DF0AAB5D03D8E73A8F00577442BE5921EBEFF51A4B979F074175D1772993FD7405B8A1CC1DCF367E0F1445FE68CD9C99

Теперь рассмотрим процесс расшифрования зашифрованной строки запроса:

protected void Page_Load(object sender, EventArgs e)

{

     NameValueCollection EncryptedQS = Request.QueryString;

     byte[] EncryptedData = GetBytes(EncryptedQS["data"]);
//Decode hexadecimal-based string

     string ClearQueryString = SymEncryption.DecryptToString(EncryptedData);

     //Split data and add the contents

     int Index;

     string[] SplittedQS = ClearQueryString.Split(new char[] { '&' });

     NameValueCollection DecryptedQS = new NameValueCollection();

     foreach (string SingleData in SplittedQS)

     {

          Index = SingleData.IndexOf('=');

          DecryptedQS.Add(

              HttpUtility.UrlDecode(SingleData.Substring(0, Index)),

              HttpUtility.UrlDecode(SingleData.Substring(Index + 1))

              );

     }

     lbMsg.Text = "mydata = " + DecryptedQS["mydata"];

     lbMsg.Text += "<br/>mytime = " + DecryptedQS["mytime"];

}

В этом коде, сначала вытаскивается зашифрованная строка запроса из URL-запроса, и она передается в методе GetBytes(), чтобы получить зашифрованные данные в виде массива байтов (byte[] EncryptedData = GetBytes(EncryptedQS["data"]);). После этого вызывается метод для расшифрования данных (string ClearQueryString = SymEncryption.DecryptToString(EncryptedData);). Расшифрованная строка типа string анализируется и ее данные передаются в объект (DecryptedQS) типа NameValueCollection, и можно получить нужные нам данные из этого объекта (например DecryptedQS["mydata"]).

3. Применение АОП в задаче шифрования строк HTTP-запроса в ASP.NET-приложении

Выше наше обсуждение показывает, что механизм шифрования и расшифрования строк запроса гораздо простой и можно применить его в наших Веб-приложениях. Проблема возникает, когда необходимо применить этот механизм в многих точках выполнения приложения, т.е. при вызове метода HttpResponse.Redirect() и при вызове процесса расшифрования многократно в разных точках выполнения приложения. Либо после внедрения приложения, возникает необходимость шифрования некоторых (или многих) строк запроса. Тогда приходится изменить код приложения, применяя выше механизм шифрования, в фиксированных точках выполнения приложения. Можно использовать силу ООП для реализации изменения. Т.е. создается какой-то модуль, например как описан в [3], но при этом в каждые из этих точек приходится изменить код для вызова этого модуля. Данная проблема решена аспектно-ориентированным программированием (АОП), т.к. видно, что шифрование и расшифрование есть две сквозной функциональности (cross-cutting concers) – функциональность, реализация которой рассредоточена по коду приложения. С помощью АОП, каждая функциональность реализуется в аспекте в виде набора действий (actions), затем определяются условия внедрения для присоединения этих действий к нужным нам точкам выполнения Веб-приложения, после этого запускается подсистема внедрения (weaver) аспектов системы Aspect.NET. Действия аспекта будут автоматически добавляться подсистемой внедрения в точки присоединения (т. е. в нужные нам точки выполнения), определенные условиями внедрения аспекта. При этом изменения целевого Веб-приложения выполняются на уровне MSIL кода.

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

Реализуются аспекты по следующей класс-диаграмме:

Рис.

Все аспекты должны унаследовать от класса Aspect платформы Aspect.NET. Базовый класс аспекта CryptoAspect содержит статическое поле SymEncryption типа SymmetricEncryption, который является шифратором, реализующим симметрический алгоритм, два статических методов GetString() и GetBytes() для шестнадцатиричного преобразования строк (как описано в разделе 2).

Класс CryptoQueryStringAspect – класс нашего аспекта, он наследует от класса CryptoAspect. В нем создаются нашие действия RedirectAction() (действие шифрования) и GetQueryStringAction() (действие расшифрования).

Действие шифрования:

[AspectAction("%instead %call HttpResponse.Redirect(string) && %args(arg[0])")]

public static void RedirectAction(string Query)

{

     string[] SplittedQuery = Query.Split(new char[] { '?' });

     string RedirectPage = SplittedQuery[0];

     string ClearQueryString = SplittedQuery[1];

     //Now encrypt Query String

     byte[] EncryptedData = SymEncryption.Encrypt(ClearQueryString);

     string NewQuery = RedirectPage + "?data=" + GetString(EncryptedData);

     HttpContext.Current.Response.Redirect(NewQuery);

}

Условие внедрения аспекта "%instead %call HttpResponse.Redirect(string) && %args(arg[0])" означает, что вместо вызова метода HttpResponse.Redirect() вызывается действие аспекта RedirectAction(string Query). То есть фактически Aspect.NET подменяет метод HttpResponse.Redirect() на действие аспекта RedirectAction() в точках выполнения, где вызывается HttpResponse.Redirect(), и при подмене аргумент действия аспекта передается аргументом целевого метода. В этом действии реализуется шифрование строки запроса ClearQueryString симметричным алгоритмом шифрования (Symmetric encryption: SymEncryption). Здесь, кроме симметричного алгоритма, можно использовать любые методы шифрования, например, Windows data protection API (DPAPI) [3, 4]. После шифрования строки запроса необходимо преобразовать ее в шестнадцатеричную строку с помощью метода GetString() [3] для передачи в URL-запрос. Наконец, перенаправляем новый URL-запрос с зашифрованной строкой запроса вызовом метода HttpResponse.Redirect().

Тогда в Веб-приложении код для перенаправления URL-запроса примерно такой:

protected void SendCommand_Click(object sender, EventArgs e)

{

     string RedirectArg = "Recipient.aspx?mydata=" + HttpUtility.UrlEncode(MyData.Text) + "&mytime=" + HttpUtility.UrlEncode(DateTime.Now.ToString());

     lbMsg.Text = "<b>" + RedirectArg + "</b>";

       Response.Redirect(RedirectArg);

}

Действие расшифрования:

[AspectAction("%instead %call HttpRequest.get_QueryString()")]

public static NameValueCollection GetQueryStringAction()

{

     HttpRequest Request = (HttpRequest)TargetObject;

     NameValueCollection EncryptedQS = Request.QueryString;

     byte[] EncryptedData = GetBytes(EncryptedQS["data"]);
//Decode hexadecimal-based string

     string ClearQueryString = SymEncryption.DecryptToString(EncryptedData);

     //Split data and add the contents

     int Index;

     string[] SplittedQS = ClearQueryString.Split(new char[] { '&' });

     NameValueCollection DecryptedQS = new NameValueCollection();

     foreach (string SingleData in SplittedQS)

     {

          Index = SingleData.IndexOf('=');

          DecryptedQS.Add(

              HttpUtility.UrlDecode(SingleData.Substring(0, Index)),

              HttpUtility.UrlDecode(SingleData.Substring(Index + 1))

              );

     }

     return DecryptedQS;

}

Условие внедрения аспекта "%instead %call HttpRequest.get_QueryString()" означает, что вместо вызова метода HttpRequest.get_QueryString() (т. е. при обращении к свойству QueryString объекта класса System.Web.HttpRequest) вызывается действие аспекта GetQueryStringAction(). В приведенном коде извлекается зашифрованная строка запроса из URL-запроса, которая передается в метод GetBytes() [3], чтобы получить зашифрованные данные в виде массива байтов

byte[] EncryptedData = GetBytes(EncryptedQS["data"]);

После этого вызывается метод для расшифровки данных

     string ClearQueryString = SymEncryption.DecryptToString(EncryptedData);

Расшифрованная строка типа string анализируется, и ее данные передаются в объект DecryptedQS типа System.Collections.Specialized.NameValueCollection, который возвращается в конце действия.

И в нашем Веб-приложении, код для получения данных от строки запроса примерно такой:

protected void Page_Load(object sender, EventArgs e)

{

     NameValueCollection QueryString = Request.QueryString;

     lbMsg.Text = "mydata = " + QueryString["mydata"];

     lbMsg.Text += "<br/>mytime = " + QueryString["mytime"];

}

Применение АОП в системе Aspect.NET для реализации шифроваиня строк запроса имеет следующие преимущества по сравнению с его реализацией без применения АОП:

  • Использование синтаксиса определения условия внедрения аспекта [2] позволяет описать множество точек присоединения, в которые необходимо добавить шифрование и расшифрование строк запроса.
  • Действия аспекта будет автоматически добавляться подсистемой внедрения (weaver) в точки присоединения (т.е. в нужные нам точки выполнения), определенные условиями внедрения аспекта. Таким образом, не требуется “ручных” вставок кода реализации шифрования и расшифрования строк запроса в этих точках выполнения. Благодаря этому, уменьшаются объем кода, вероятность программных ошибок, время и стоимость разработки.
  • Никаких изменений кода в целевом Веб-приложении не требуется.
  • Упрощается сопровождение и расширение Веб-приложения. Новые требования реализуются в аспекте, затем внедряются в целевое Веб-приложение. Изменение требований осуществляется также в коде реализации аспекта.
  • Веб-приложения полностью работоспособны без применения аспектов. При этом шифрование и расшифрование строк запроса, реализованные в аспекте, отсутствуют в целевых Веб-приложениях.

4. Заключение

В данной работе описана и анализирована задача шифрования строк запроса в ASP.NET-приложении. Предложен метод применения аспектно-ориентированного программирования для решения описанной задачи. Разработан аспект шифрования строк запроса в системе Aspect.NET. Благодаря разработанному аспекту, уменьшаются объем кода, вероятность программных ошибок, время и стоимость разработки.

Текст статьи
  1. Wikipedia http://en.wikipedia.org/wiki/Query_string.
  2. Сафонов В. О. Практическое руководство по системе аспектно-ориентированного программирования Aspect.NET [Текст].
  3. Matthew MacDonald and Mario Szpuszta. Pro ASP.NET 3.5 in C# 2008.
  4. Windows Data Protection: http://msdn.microsoft.com/en-us/library/ms995355.aspx.
Список литературы
Ведется прием статей
Прием материалов
c 27 ноября по 03 декабря
Осталось 6 дней до окончания
Публикация электронной версии статьи происходит сразу после оплаты
Справка о публикации
сразу после оплаты
Размещение электронной версии журнала
07 декабря
Загрузка в eLibrary
07 декабря
Рассылка печатных экземпляров
15 декабря