• Программирование
  • C++
  • Веб-сервисы, защищенные посредством промежуточного программного обеспечения, ориентированного на обработку сообщений

Веб-сервисы, защищенные посредством промежуточного программного обеспечения, ориентированного на обработку сообщений - Обзор WSE

ОГЛАВЛЕНИЕ

Обзор WSE

WSE – механизм обработки данных, подвергающий (по инструкции) серии преобразований исходящие сообщения Soap, после их сериализации инфраструктурой веб-сервисов .NET, и, наоборот, подвергает дополнительным преобразованиям входящие сообщения Soap перед их десериализацией в объекты / параметры. Эти преобразования подразумевают добавление дополнительных заголовков Soap и иногда (как при шифровании) также изменения тела сообщения Soap.
WSE предоставляет относительно низкоуровневый API, подходящий для подключения и воздействия на работу клиента и сервера. Отличная статья, рассматривающая внутренние механизмы WSE, находится в . В следующих разделах перечислены основные моменты, образующие контекст изменений, производимых в клиенте и сервере.

Физическая реализация WSE 1.0 – единственная установленная в GAC сборка под именем Microsoft.Web.Services.dll, реализующая большинство из трех упомянутых выше спецификаций.

Архитектура конвейера фильтра WSE

Единственное важнейшее изменение по сравнению с бета-версией программного обеспечения (именуемого WSDK, выпущенного в августе 2002 года) в WSE 1.0 состоит в том, что функциональность, осуществляющая преобразования сообщений Soap, была перекомпонована в серию фильтров. Эти фильтры принадлежат  одному из двух видов. Выходные фильтры сосредоточены на обработке исходящего сообщения Soap, тогда как входные фильтры обрабатывают входящие сообщения Soap.

Входной фильтр

Выходной фильтр

Назначение

TraceInputFilter

TraceOutputFilter

Запись сообщения в журнал для отладки

SecurityInputFilter

SecurityOutputFilter

Поддержка аутентификации, подписи и шифрования

TimestampInputFilter

TimestampOutputFilter

Поддержка метки времени

ReferralInputFilter

ReferralOutputFilter

Динамические обновления направлений маршрутизации

RoutingInputFilter

RoutingOutputFilter

Маршрутизация сообщений

Каждый набор фильтров сгруппирован в конвейер входного или выходного фильтра, по умолчанию запускающий фильтры в следующем порядке (для потока от клиента к серверу):

Рисунок 11. Архитектура конвейера фильтра подробнее (поток клиент -> сервер)

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

Каждый конвейер производит действия над оболочкой Soap, чтобы выработать разное содержимое внутри данной оболочки.

Концепция конвейера в WSE является очень мощной, так как конвейерами можно управлять программно для удаления или переупорядочивания стадий, и новые пользовательские стадии конвейера могут вводиться на основе предоставляемых WSE базовых классов SoapOutputFilter и / или SoapInputFilter.

Объединение выполнения конвейера WSE входного и выходного фильтров со стандартными механизмами сериализации и доставки Soap управляется двумя разными способами:
•    Для клиентов веб-сервиса ASP.NET управление осуществляется путем наследования генерируемого класса посредника от нового базового класса посредника под именем Microsoft.Web.Services.WebServicesClientProtocol.
•    Для веб-сервисов ASP.NET было предоставлено новое серверное расширение Soap в форме Microsoft.Web.Services.WebServicesExtension.

Назначение нового базового класса посредника и серверного расширения – располагаться между протоколом передачи и модулями сериализации/десериализации Soap, перехватывая сообщения и строя / анализируя заголовки Soap по необходимости. В результате крайне вероятно, что при использовании, например, проверки подлинности подписи в веб-сервисе, в случае если данная проверка обнаружит искажение и сгенерирует соответствующую ошибку Soap, ни одна строка программного кода приложения не будет вызвана к моменту генерации ошибки. Такой прием хорошо изолирует код приложения от кода инфраструктуры WSE, что является привлекательной выгодой.

SoapWebRequest и SoapWebResponse в WSE

WSE сам использует инфраструктуру подключаемого протокола веб-сервисов .NET для предоставления двух новых коммуникационных классов, именуемых SoapWebRequest и SoapWebResponse. Как и в случае версий MSMQ и MQ, эти классы получены из System.Net.WebRequest и System.Net.WebResponse соответственно.

Класс SoapWebRequest разбирает на части поток запроса, содержащий стандартное сериализованное сообщение Soap, и помещает его части в экземпляр другого предоставляемого WSE класса под именем SoapEnvelope, производного от System.Xml.XmlDocument. Затем он запускает стандартный конвейер выходного фильтра, обрабатывающий содержимое оболочки, как описано в разделе выше.

В среде разработки важное значение имеет объединение основанных на очереди классов с новыми классами интернет-запроса/ответа Soap, и особенно перехват получаемого потока Soap для использования обработчиками протоколов.
Однако для серверной службы SoapWebRequest и SoapWebResponse не нужны, так как работа происходит вне клиента или инфраструктуры сервера ASP.NET, и приходится вручную выполнять обработку WSE.

SoapContext WSE для коммуникации

Рассматривалось функционирование WSE, но не разбиралось, как на данную обработку влияет разрабатываемый код приложения. Класс SoapContext – механизм, при помощи которого код приложения и код форматирования WSE не прямо взаимодействуют. Это осуществляется одним из двух способов, в зависимости от точки в процессе:
•    При построении заголовков Soap в ходе обработки исходящих сообщений приложение сначала  вставляет атрибуты SoapContext, становящиеся инструкциями, по которым работают фильтры, т.е. в данном случае атрибуты SoapContext формируют содержимое исходящего сообщения Soap.
•    При анализе заголовков в ходе обработки входящих сообщений WSE вставляет SoapContext в результаты фильтрации входящих сообщений перед передачей управления серверному приложению, т.е. при этом содержимое входящего сообщения Soap формирует настройку SoapContext.

Класс содержит несколько нужных атрибутов:

Рисунок 12. Контекст WSE Soap

Оболочка содержит XML, маркирующий "выполняемую работу" во время работы фильтров. Атрибут защиты – место для прикрепления информации ключа шифрования и информации подписи. Вложения хранят вложения DIME (больше информации об этом далее).

Клиент и сервер поддерживают предоставление SoapContext через класс SoapWebRequest в клиенте и через статические классы HttpSoapContext.RequestContext и HttpSoapContext.ResponseContext в вебсервисе ASP.NET. Клиент должен уметь управлять SoapContext в надежде, что он будет влиять на фильтры. Обычно серверная служба не размещается на площадке ASP.NET, и следует надеяться, что есть другой способ извлекать SoapContext в данной среде.

Архитектура SecurityProvider

Как объясняется выше, инфраструктура WSE основана на перехвате. и она отделяет проверку безопасности от программного кода приложения. Это поднимает интересный вопрос, так как передающая сторона генерирует "маркер пользователя", встраивая имя пользователя и связанный с ним пароль (хешированный), подписывая и симметрично шифруя тело Soap, и т.д., но необходимо знать имя пользователя / пароль и соответствующий ключ для расшифровки на целевом абонентском пункте, если полезная нагрузка Soap подлежит успешной обработке.

WSE предоставляет зацепки в обработке входящих сообщений для ее упрощения. В частности, он предоставляет два интерфейса –  IPasswordProvider и IDecryptionKeyProvider. Первый реализуется кодом приложения, чтобы предоставлять пароль конкретному пользователю, обычно из базы данных пользователей. Второй реализуется, чтобы предоставлять ключ расшифровки. Оба интерфейса вызываются в ходе обработки входящего сообщения WSE, если заголовки Soap показывают, что сообщение было подписано и / или зашифровано:

Рисунок 13. Архитектура поставщика средств обеспечения безопасности WSE

WSE позволяет разработчику писать код для этих двух зацепок и сообщать о нем среде выполнения WSE через информацию в соответствующем конфигурационном файле приложения. Существует три возможных вида указанного файла 3 в зависимости от среды:
•    Для веб-сервисов ASP.NET  файл web.config
•    Для клиентов ASP.NET - файл app.config
•    Для автономных программ – конфигурационный файл приложения

Входы для обоих поставщиков выглядят примерно так:

<microsoft.web.services>
  <security>
    <passwordProvider type="WSCommon.WSESecurityProvider, WSAltRouteBEFake" />
    <decryptionKeyProvider
                      type="WSCommon.WSESecurityProvider, WSAltRouteBEFake" />
  </security>
</microsoft.web.services>

Каждый вход показывает имя типа (класс) и сборку, в которой находится тип. При двусторонней защите (т.е. сообщения запроса и ответа Soap) одинаковые входы находятся на конце сервиса (используется при приеме запроса Soap) и на конце клиента (используется при приеме ответа Soap).

Инструмент задания параметров WSE 1.0 для .NET

Единственная проблема с гибкостью перехвата этих поставщиков в том, что очень просто ошибиться. Для этого Microsoft создал неподдерживаемое VS.NET расширение , упрощающее добавление параметров. После его установки появляется новая команда контекстного меню при щелчке правой кнопкой мыши по узлу на уровне проекта в Проводнике решения. Эта команда позволяет разработчикам активировать защиту WSE, затем задавать информацию о поставщике в диалоговом окне и вставлять ее в соответствующий конфигурационный файл. В данном случае указывается, что серверная служба содержит единственный класс с именем WSCommon.WSESecurityProvider, обрабатывающий разрешение пароля и ключа расшифровки.

Рисунок 14. Инструмент задания параметров WSE

Авторы считают, что инструмент работает только с WSE 1.0, поэтому, вероятно, не будет работать с будущими обновлениями WSE, но к тому времени вы хорошо усвоите параметры конфигурации.

Обновление каркаса клиента для использования WSE

Необходимо внести определенный ряд изменений в каркас, чтобы он мог обрабатывать запросы WSE и запросы, не относящиеся к WSE.

Добавление дополнительного класса в MySoapClientProtocol

Так как WSE требует получения посредника из Microsoft.Web.Services.WebServicesClientProtocol для вызова обработки конвейера фильтра, нужно внедрить в каркас дополнительный класс с именем MyWSESoapClientProtocol, поддерживающий свойство ResponseUrl:

Рисунок 15. Доступ к WSE из клиента

Изменения в генерируемом посреднике

Единственные изменения в коде посредника подразумевают перебазирование в MyWSESoapClientProtocol и небольшое изменение вызова переопределенного метода GetWebRequest() и удаление вызова переопределенного метода

GetWebResponse():
public class Service1 : MyWSESoapClientProtocol
{
    // Все вызовы методов API остаются неизменными

    …

    // Переопределенные части
    protected override WebRequest GetWebRequest(Uri uri)
    {
        // Регистрируем схему если нужно
        ProxyCommon.RegisterRelevantScheme(uri, this);

        // Выполняем стандартные операции с использованием SoapWebRequest,
        // объединяемого с классами WebRequest / WebResponse MSMQ / MQ
        // когда нужно.
        return base.GetWebRequest(uri);
    }
}

Это работает благодаря SoapWebRequest, перехватывающему код обработки фильтра WSE, и затем передающему полученную оболочку Soap выбранному протоколу передачи дальше по линии для передачи. Остальной код каркаса (MSMQWebRequest, MQWebRequest, и т.д.) не меняется.

Изменения в вызывающем коде

Приложение должно устанавливать объект SoapContext, связанный с выполняемым вызовом. Это достигается путем добавления в клиент нового закрытого метода, добавляющего основную информацию в SoapContext. Затем вызывающий оператор запускает вызов следующим образом:

// Создаем посредник сервиса
objHelloWorldService = new localhostWSE.Service1();

// Устанавливаем URI
objHelloWorldService.Url = objHelloWorldService.FormatCustomUri(vstrURI);

// Пополняем контекст запроса средствами WSE посредством универсального алгоритма
// Он включает в себя:
//
//    * Время жизни 60с
//    * Данные имени пользователя и пароля
//    * Шифрование и подписывание
//
// Для достижения этого нужно добавить атрибуты в изначально пустой
// SoapContext!!
ProxyCommonWSE.SetupSOAPRequestContext(objHelloWorldService.RequestSoapContext);

// Выполняем метод и получаем возвращаемый объект
string strResult = objHelloWorldService.HelloWorld("Simon");

// Убеждаемся, что запрос SOAP был принят с правильными учетными данными
// и подписью посредством универсального алгоритма – путем проведения
// опроса сгенерированного SoapContext!!
ProxyCommonWSE.ValidateCredentials(objHelloWorldService.ResponseSoapContext);

Чтобы дать представление о том, как заполняется объект SoapContext, ниже приводится часть закрытого метода

SetupSOAPRequestContext():


// Получаем симметричный ключ шифрования, с помощью которого шифруется тело Soap
EncryptionKey encKey = GetEncryptionKey();



// Создаем маркер пользователя на основе счётчика времени и статическое имя
// пользователя
UsernameToken userToken = new UsernameToken(
                strUserName,
                CalculatePasswordForProxyUser(strUserName),
                PasswordOption.SendHashed);

// Добавляем маркер пользователя в контекст Soap
vobjSoapContext.Security.Tokens.Add(userToken);

// Запрашиваем подпись для сообщения (на основе маркера пользователя)
vobjSoapContext.Security.Elements.Add(
             new Microsoft.Web.Services.Security.Signature(userToken));

// Просим зашифровать тело Soap (с помощью отдельного симметричного ключа)
vobjSoapContext.Security.Elements.Add(
            new Microsoft.Web.Services.Security.EncryptedData(encKey));

// Устанавливаем окончание срока действия сообщения SOAP
vobjSoapContext.Timestamp.Ttl = SOAP_MSG_EXPIRY_TIME;    // 60с

Основным преимуществом является возможность отправлять 160-битный хэш пароля SHA-1 (необратимый). Хэш сравнивается с вычисленной версией на удаленной стороне, после того как зацепка SecurityProvider была вызвана на той стороне. Подписание и шифрование запрашиваются с использованием разных ключей, и срок действия сообщения устанавливается в SOAP_MSG_EXPIRY_TIME (60 секунд).

Нужно соблюдать осторожность при шифровании и / или подписании сообщения Soap. Можно задавать разную степень разбиения, с которой должно защищаться сообщение – от всего сообщения вплоть до отдельных элементов сообщения Soap. Хотя здесь не используются средства маршрутизации в WSE, читатель должен сознавать, что нужно соблюдать осторожность при защите сообщения, проходящего через промежуточные узлы по пути к конечному пункту назначения.

Причина в том, что модель обработки Soap позволяет удалять заголовки Soap на промежуточных стадиях. Поэтому, например, если все сообщение (включая заголовки) подписывается, и затем передается из обрабатывающего узла A к узлу C через узел B, есть вероятность, что узел B случайно сделает сообщение недействительным путем удаления заголовка, что вызовет неудачную проверку подлинности подписи в конечном пункте назначения.

Наблюдается странность при запуске посредника, полученного из класса WebServicesClientProtocol (через MyWSESoapClientProtocol) в сценарии, когда ничего не устанавливается в объекте SoapContext перед выполнением вызова. В этом случае следовало бы ожидать, что приложение-получатель без поддержки WSE должно суметь успешно использовать сообщение Soap. Однако, дело обстоит не так. В настоящее время замечены две проблемы, основанные на странном поведении заголовка:
•    Вы получаете исключение "Путь не содержит элемент <действие>" при запуске вызова любого метода веб-сервисов. По-видимому, это происходит только в случае пользовательского протокола передачи – стандартный протокол передачи HTTP внутренне справляется с данным глюком.
•    При установке пути явно посредством заполнения направления маршрутизации чем-то вроде vobjSoapContext.Path.Action = string.Empty предполагается, что больше ничего не будет добавлено в сообщение Soap, и что поэтому сервер не станет запускать набор инструментов приведения в соответствие с WSE, и, следовательно, сообщения будут успешно использоваться. Однако дело обстоит не так. Наряду с данными маршрутизации, добавляется информация заголовка метки времени, и серверные службы Java странно реагируют на это и на указанные данные маршрутизации, преимущественно на основании атрибута soap:mustUnderstand="1", вставленного в часть Soap, обозначающего данные маршрутизации: этот атрибут говорит приемнику “прервать обработку”, если он не может понять смысл конкретного заголовка, с которым связан этот атрибут.

Из-за указанных глюков вместо получения посредника из единственного класса (MyWSESoapClientProtocol) и использования наличия или отсутствия вызовов метода SetupSOAPRequestContext()для управления защитой сообщений Soap сейчас приходится создавать две версии посредника: первая получается из MyWSESoapClientProtocol, а вторая получается из MySoapClientProtocol. Нужно знать, когда использовать каждую версию (преимущественно) во время разработки. Эти недостатки будут устранены по мере развития стандартов.

На флаг soap:mustUnderstand может воздействовать отправитель сообщения, что позволяет обойти часть проблем.