Продолжается подписка на наши издания! Вы не забыли подписаться?

Упрощение аутентификации с помощью WSE 3.0

Автор: Мартин Кулов
Опубликовано: 19.04.2007

Microsoft предоставляет средства создания Web-сервисов с момента выхода первой версии Visual Studio.NET в 2000 году.

Как и многие другие технологии Microsoft, первая реализация Web-сервисов – так называемые ASMX Web-сервисы – показывали путь к весьма плодотворным решениям, но не содержали ключевых аспектов функциональности, необходимых для создания действительно надежных и безопасных решений. Web Services Extensions 3.0 – это новая версия, позволяющая справиться со многими недостатками ASMX Web-сервисов. Одна из ключевых новых возможностей – возможность создавать готовые решения, поддерживающие аутентификацию в Web-сервисах. Я расскажу, как это делается.

Поучительно сравнить то, что Microsoft делает доступным теперь, с тем, что существовало в момент первоначального выпуска VS.NET. Несомненно, с помощью Visual Studio.NET с самого начала было просто создавать Web-сервисы. Рассмотрим, например, процесс создания ASMX Web-сервиса. Сначала вы создаете проект, который будет содержать Web-сервис. Далее используются атрибуты WebService и WebMethod, чтобы показать классы и методы, которые хотите вызывать удаленно.

Способ работы Web-сервисов довольно прост. Они посылают SOAP-сообщение, инкапсулированное в теле http-пакета. Ответ, возвращаемый сервером, – HTTP-пакет с SOAP-сообщением.

Создание Web-сервиса – простая задача, а обеспечение безопасности – нет. И, скорее всего, вам потребуется обезопасить доступ к любому Web-сервису и реализовать разные уровни аутентификации. Здесь ASMX Web-сервисы бессильны.

Microsoft внес изменения, упрощающие процесс отладки, например, использование методов GET и POST, определенных протоколом HTTP, для доступа к Web-сервисам. Это замечательно работает, но сегодня перед разработчиками стоят некие критические проблемы, требующие решения.

Во-первых, Web-сервис не должен зависеть от транспортного протокола. Вы должны просто передавать SOAP-сообщение, независимо от протокола, и позволить транспортному слою разбираться с деталями протоколов, будь то HTTP, TCP, UDP, MSMQ или даже SMTP.

Во-вторых, и это критично: Web-сервис нужно защитить от неавторизованного доступа. Отправляемое SOAP-сообщение должно быть зашифровано (или, на худой конец, подписано), а доступ к сервису должен предоставляться в зависимости от прав пользователя. Здесь возможности независимости от протоколов выглядят просто блестяще. Если вы предоставляете механизм шифрования и подписи SOAP-сообщения, гарантируется, что этот механизм будет работать независимо от используемого протокола. Более того: можно менять механизмы аутентификации, нужно только, чтобы клиент и сервер знали, как обрабатывать SOAP-сообщение. К сожалению, ASPX Web-сервисы не способны на это, так как тесно связаны с протоколом HTTP.

Старые подходы против новых

Возможно, полезно посмотреть, как разработчики раньше работали с безопасностью в Web-сервисах, прежде чем обратить внимание на то, как это делается сейчас. Чтобы обезопасить ASMX Web-сервис, вы можете реализовать собственное решение по шифрованию данных, использовать какой-либо сервис состояния для хранения информации о пользователях между запросами, или воспользоваться безопасностью уровня транспорта, например, HTTPS.

Одна часть этого решения может потребовать шифрования содержимого передаваемых параметров или получения SSL-сертификата сервера. Вы можете реализовать собственную схему аутентификации, передавая хеш-ключ в качестве первого параметра при каждом вызове метода. Вы генерируете хеш-ключ в методе Login и сохраняете его в БД. Если ключ имеется в БД, и пользователь имеет право обращаться к методу, запрос выполняется удачно. Именно так я поступил в первом проекте Web-сервиса, выполнявшемся нашей командой. Нам тогда кто-то сказал, что не нужно смешивать состояние сессии IIS с Web-сервисами, и мы ему поверили. Это оказалось неправдой, но мы все равно сохранили собственное решение. Как я помню, мы потратили около недели на реализацию и отладку этого решения, так как хотели, чтобы оно работало в Web Farm. Достаточно сказать, что создание собственного решения означает также трату существенного количества времени на аудит кода, тестирование и проверку безопасности этого решения.

Microsoft активно пытается решить эти вопросы, начиная с первой версии ASMX Web-сервисов. Например, решения по аутентификации Web-сервисов вошли в Web Service Enhancements (WSE) 1.0. WSE – это add-on к Visual Studio .NET; на Web-сайте Microsoft можно найти последнюю версию, WSE 3.0. Одна из интереснейших возможностей WSE 3.0 – простой в использовании, расширяемый фреймворк, обеспечивающий безопасность и аутентификацию.

Для создания этого фреймворка Microsoft собрал информацию по наиболее часто использующимся способам обеспечения безопасности Web-сервисов. Использовались также сообщения пользователей на различных форумах и в новостных группах, описывающие часто используемые сценарии. Эти сценарии отражают наиболее часто используемые, основные способы реализации обеспечения безопасности и аутентификации Web-сервисов на текущий момент. Именно поэтому они получили название Turnkey Security Scenarios – сценарии безопасности «под ключ».

Документация WSE 3.0 называет эти сценарии «Turnkey Security Assertions», но термин «сценарий» гораздо шире распространен в сообществе разработчиков Web-сервисов, так что я буду использовать именно его. В готовых сценариях хорошо то, что их можно применять декларативно, позволяя администратору системы менять их во время работы сервиса. Декларативная модель позволяет также программно расширять существующие сценарии или даже создавать собственные сценарии. Еще одно преимущество: топология сети может измениться – сервер может переехать к другому провайдеру – но вам не потребуется заново развертывать сервис.

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

Начнем с мастера (wizard)

Начнем с использования мастера WSE, чтобы включить использование политики Web-сервисом. После установки WSE 3.0 щелкните правой кнопкой по Properties в проекте Web-сервиса и выберите "WSE Settings 3.0" из контекстного меню. На первой закладке нужно пометить "Enable this project for Web Service Enhancements" и "Enable Microsoft Web Services Enhancements Soap Protocol Factory". На закладке Policy пометьте "Enable Policy", и будет показано имя файла политики, который нужно использовать. Этот файл создается в той же папке, что и файл web.config. Щелкните по кнопке Add, чтобы добавить новую политику для приложения, и укажите какое-нибудь дружественное имя, хотя бы MyPolicy. Это приведет к открытию WSE Security Settings Wizard. Нажмите Next. Это откроет страницу Authentication Settings, содержащую наиболее интересные опции (см. рисунок 1).


Рисунок 1.

Убедитесь, что вы выбрали "Secure a service application". Заметьте, что вы работаете над проектом Web-сервиса, а не клиента, обращающегося к Web-сервису. Чтобы создать файл политики для клиентской стороны, нужно снова запустить мастер и выбрать "Secure a client application".

Нижняя часть мастера предоставляет выбор из четырех методов аутентификации клиента: анонимный (anonimous), по имени пользователя (username), по сертификату и аутентификацию Windows. Нажмите Next и оставьте все остальные опции неизменными. Каждый метод аутентификации реализуется с использованием готового сценария WSE 3.0. Разберем, что означает каждый из вариантов.

Анонимный метод аутентификации реализуется классом AnonymousForCertificateAssertion. Этот метод используется для работы с анонимными клиентами. Например, его можно использовать, если неважно, кто именно обращается к серверу. Клиенты не аутентифицируются, так что вы заботитесь только о том, что связь безопасна, и не может быть отслежена или подделана. Безопасность связи обеспечивается серверным сертификатом X.509. Это единственный сертификат, который вы должны установить на сервере для обеспечения безопасности коммуникаций по этому сценарию. Сертификат позволяет шифровать и подписывать SOAP-сообщение целиком или отдельные его части, например, только тело и заголовок сообщения. Элемент <protection> дает способ указать, какие части сообщения нужно подписать и зашифровать. Как видите, сертификат сервера определен с помощью элемента X.509 как дочерний для элемента <serviceToken>. Сертификат можно найти по имени и месту хранения; в данном случае сертификат WSE2QuickStartServer находится в хранилище сертификатов Local Machine. Метод анонимной аутентификации – один из распространенных сценариев, так что этот метод является одним из наиболее часто используемых методов аутентификации (см. листинг 1).

Листинг 1.
<policies>
   <extensions>
      <extension name="anonymousForCertificateSecurity" 
         type="Microsoft.Web.Services3.Design.
         AnonymousForCertificateAssertion, 
         Microsoft.Web.Services3,
         Version=3.0.0.0, Culture=neutral, 
         PublicKeyToken=31bf3856ad364e35" />
      <extension name="x509" 
         type="Microsoft.Web.Services3.Design.
         X509TokenProvider, Microsoft.Web.Services3, 
         Version=3.0.0.0, Culture=neutral, 
         PublicKeyToken=31bf3856ad364e35" />
      <extension name="requireActionHeader"
         type="Microsoft.Web.Services3.Design.
         RequireActionHeaderAssertion, 
         Microsoft.Web.Services3, Version=3.0.0.0, 
         Culture=neutral, 
         PublicKeyToken=31bf3856ad364e35" />
   </extensions>
   <policy name="ServerPolicy">
      <anonymousForCertificateSecurity 
         establishSecurityContext="false" 
         renewExpiredSecurityContext="true" 
         requireSignatureConfirmation="false" 
         messageProtectionOrder="SignBeforeEncrypt" 
         requireDerivedKeys="true" >
      <serviceToken>
      <x509
         storeLocation="LocalMachine"
         storeName="My"
         findValue="CN=WSE2QuickStartServer"
         findType="FindBySubjectDistinguishedName" />
      </serviceToken>
      <protection>
      <request
         signatureOptions="IncludeAddressing, 
         IncludeTimestamp, IncludeSoapBody"
         encryptBody="true" />
      <response 
         signatureOptions="IncludeAddressing, 
         IncludeTimestamp, IncludeSoapBody"
         encryptBody="true" />
      <fault
         signatureOptions="IncludeAddressing, 
         IncludeTimestamp, IncludeSoapBody"
         encryptBody="false" />
      </protection>
      </anonymousForCertificateSecurity>
      <requireActionHeader />
   </policy>
</policies>

Метод аутентификации Username реализуется классом UsernameForCertificateAssertion. Этот метод в основном используется так же, как метод анонимной аутентификации, но его можно использовать для авторизации клиентов с помощью имени пользователя и пароля. Этот метод аутентификации использует по умолчанию систему безопасности Windows. Он использует Active Directory, если сервер входит в ее состав.

Опять же, сервер предоставляет сертификат X.509, указанный в <serviceToken> (см. листинг 2). Клиент для доступа к сервису должен сообщить имя пользователя и пароль. Я рекомендую предоставлять реквизиты пользователей программно, как будет показано ниже.

Листинг 2.
<policy name="MyPolicyUsername">
   <usernameForCertificateSecurity 
      establishSecurityContext="true" 
      renewExpiredSecurityContext="true" 
      requireSignatureConfirmation="false" 
      messageProtectionOrder="SignBeforeEncrypt" 
      requireDerivedKeys="true" ttlInSeconds="300">
   <serviceToken>
   <x509 storeLocation="LocalMachine" storeName="My" 
      findValue="CN=WSE2QuickStartServer" 
      findType="FindBySubjectDistinguishedName" />
      </serviceToken>
      <protection>
      <request signatureOptions="IncludeAddressing, 
         IncludeTimestamp, IncludeSoapBody" 
         encryptBody="true" />
      <response signatureOptions="IncludeAddressing, 
         IncludeTimestamp, IncludeSoapBody" 
         encryptBody="true" />
      <fault signatureOptions="IncludeAddressing, 
         IncludeTimestamp, IncludeSoapBody" 
         encryptBody="false" />
      </protection>
      </usernameForCertificateSecurity>
   <requireActionHeader />
</policy>

Метод аутентификации по сертификату используется, если и клиент, и сервер имеют сертификаты X.509, позволяющие им аутентифицировать друг друга. Это редкое решение, поскольку наличие клиентских сертификатов требует инфраструктуры PPK (как мы понимаем, автор имел в виду аббревиатуру от Private-Public Key – прим. ред.), или, что встречается еще реже, покупки сертификата каждым клиентом. Требовать доверенный сертификат у каждого отдельного клиента просто нерационально для Web-сайта. Это решение более пригодно в закрытых сообществах, внутренних сетях или при связи между подразделениями, в которых можно реализовать инфраструктуру PPK. Метод аутентификации по сертификату реализуется классом MutualCertificate11Assertion, требующим использования спецификации WS-Security 1.1. Если ваше решение требует использования WS-Security 1.0 и 1.1, вместо класса MutualCertificate11Assertion нужно использовать класс MutualCertificate10Assertion. В документации этот класс называют MutualCertificate10.

Использование Windows-аутентификации

Windows-аутентификация – последний вариант на странице мастера. Этот вариант аутентификации использует отдельный сервер реквизитов Kerberos, к которому клиент и сервер могут обращаться для проверки реквизитов. Сервер реквизитов издает сервисные билеты (service tickets) Kerberos, инкапсулируемые в SOAP-сообщениях. Принимающая сторона может запросить Kerberos-сервер о действительности полученного билета. Можно настроить Kerberos-серверы так, чтобы они допускали делегирование, что делает их полезными при реализации корпоративного уровня безопасности или системы «единой подписи» (single sign on, SSO). Мастер WSE не позволяет настроить установки для билета Kerberos, но это можно сделать вручную, в файле политики. Элемент Kerberos показан в листинге 3.

Листинг 3.
<policy name="MyPolicyWindows">
   <kerberosSecurity establishSecurityContext="true" 
      renewExpiredSecurityContext="true" 
      requireSignatureConfirmation="false" 
      messageProtectionOrder="SignBeforeEncrypt" 
      requireDerivedKeys="true" ttlInSeconds="300">
   <token>
   <kerberos targetPrincipal="nasd/martin@devbg" 
      impersonationLevel="Identification"/>
   </token>
   <protection>
   <request signatureOptions="IncludeAddressing, 
      IncludeTimestamp, IncludeSoapBody" 
      encryptBody="true" />
   <response signatureOptions="IncludeAddressing, 
      IncludeTimestamp, IncludeSoapBody" 
      encryptBody="true" />
   <fault signatureOptions="IncludeAddressing, 
      IncludeTimestamp, IncludeSoapBody" 
      encryptBody="false" />
   </protection>
   </kerberosSecurity>
   <requireActionHeader />
</policy>

WSE 3.0 содержит еще один готовый сценарий, но его нет в мастере. Он реализуется классом UsernameOverTransportAssertion и позволяет аутентифицировать клиентов с помощью текстовых имени пользователя и пароля, выполняя шифрование сообщений и защиту на уровне транспорта. Однако WSE не проверяет, обеспечивается ли безопасность на уровне транспорта, так что при использовании этого сценария необходимо соблюдать осторожность. Вы же не хотите, чтобы ваши пароли было легко перехватить при передаче. Использовать сценарий UsernameOverTransport просто:

<policy name="ClientPolicy">
   <usernameOverTransportSecurity />
   <requireActionHeader />
</policy>

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

Я уже упоминал, что я предпочитаю задавать имена пользователей и пароли программно. Можно сделать это в файле политики, но это не рекомендуется при использовании сценария UsernameOverTransport, поскольку пароль нигде не должен храниться в текстовом виде.

После создания политики для сервиса нужно сделать то же самое для клиента. Наличие обеих политик позволяет применить их в классе PolicyAttribute Web-сервиса, а также в прокси-классе Web-сервиса:

[Policy("MyPolicyUsername")].

Можно также вызвать метод proxy.SetPolicy("MyPolicyUsername") для прокси-класса Web-сервиса. Задание имени пользователя и пароля для клиентского прокси описано в документации WSE:

// Задание логина в коде
UsernameToken token = null;

string username = Environment.UserName;
string password = GetUsersPassword(username);
token = new UsernameToken(username, password);

// Имя пользователя и пароль задаются в коде
proxy.SetClientCredential<UsernameToken>(token);

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

Во-первых, важно помнить, что Web-сервисы задуманы как не имеющие состояния (stateless). Они обычно не хранят состояние сессии между запросами. Это значит, что процесс аутентификации должен повторяться при каждом вызове. Для улучшения производительности WSE 3.0 реализует спецификацию WS-SecureConversation, предоставляющую симметричные ключи для ускорения аутентификации после первого аутентифицированного SOAP-сообщения.

Кроме того, средство WSE 3.0 Settings содержит закладку Diagnostics, позволяющую задать файлы для сохранения входящих и исходящих SOAP-сообщений в процессе тестирования. Я очень рекомендую использовать эту возможность и посмотреть, как выглядят ваши сообщения. Это может помочь лучше понять эффект каждой задаваемой в файле политики опции.


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

Copyright © 1994-2016 ООО "К-Пресс"