Веб-сервисы, защищенные посредством промежуточного программного обеспечения, ориентированного на обработку сообщений - Обзор 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 может воздействовать отправитель сообщения, что позволяет обойти часть проблем.