Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #106, ОКТЯБРЬ 2007 г.

Пацанский Web 2.0

Николай Байбородин

Хакер, номер #106, стр. 120

(baiborodin@gmail.com, www.glassfish.ru)

Поднимаем приватный веб-сервис

Сегодня термин Web 2.0 не упоминают в своих разговорах разве что бабушки в «булошной». Невольно возникает ощущение, что шумиха, раздутая вокруг него, на самом деле искусственна. Но, с другой стороны, в технологии Web 2.0 серьезные корпорации вкладывают еще более серьезные деньги. Значит, что-то все-таки в нем есть. И, если это действительно не пустышка, выдуманная маркетологами, почему бы и нам не взять на вооружение некоторые из его технологий? Например, веб-сервисы...

Грязные сервисы

Наверняка ты уже слышал о парадигме «Приложения как сервисы». Действительно удобно, когда нет необходимости грузить себя проблемами совместимости программного обеспечения и производительности аппаратной части. Раз так, то почему бы и тебе не воспользоваться их преимуществами? Вариантов может быть множество – я не стану читать тебе проповедь о моральной стороне вопроса. По большому счету меня совершенно не интересует, где и как ты собираешься применять рассмотренные технологии. Цель этой статьи совершенно в другом – в том, чтобы обратить внимание на вопрос защиты создаваемых сервисов. Эта тема многогранна и при всем желании не уместится в рамки журнальной статьи. Поэтому я предлагаю остановиться на одном аспекте безопасности, а именно на взаимодействии веб-сервиса с системой аутентификации пользователей.

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

Методы обеспечения приватности

Вопросы безопасности веб-сервисов регулируются одним из стандартов некоммерческой организации OASIS, который называется (кто бы мог подумать) WS-Security. Мы будем иметь дело с реализацией WS-Security от компании Microsoft, а именно с версией WSE 3.0, которая прекрасно работает в связке с IDE Visual Studio.

Существует несколько способов обеспечения безопасности. Самый простой из них - это обмен данными через SSL совместно с использованием сертификатов на стороне клиента, гарантирующим, что потенциальный потребитель услуг обладает достаточными полномочиями для взаимодействия с ним. Недостаток этого метода, как ты уже догадался, кроется в необходимости организации управления сертификатами. К тому же никто не отменял такую разновидность атаки, как man-in-the-middlе. Так что это не наш метод. Другой подход заключается в том, чтобы осуществлять процесс идентификации пользователя одновременно с процессом удаленного вызова процедур. Чтобы лучше понять принцип действия последнего способа, представь, что у нашего веб-сервиса есть такой метод, который, приняв в качестве параметра, допустим, ANSI-сокращение наименования страны, отправляет клиенту ответ, содержащий список валидных приватных проксиков, находящихся именно в этой стране. Прототип этого метода будет выглядеть следующим образом:

public String[] GetProxyList(String country)

Для того чтобы отрубить разным халявщикам доступ к нашему сервису, достаточно внести минимальные изменения в приведенный выше метод:

public String[] GetProxyList (String country, username, password)

Недостаток подобного подхода очевиден: передаваемые удаленному методу параметры username и password сможет перехватить даже пионер, и вся защита пойдет лесом. А теперь возьмем два предыдущих подхода и тщательно их перемешаем до образования однородной массы. Полюбуйся на результат – коктейль SSL & RPC, название которому можешь придумать сам.

Но и в этом случае нас ждет нехилая засада. Во-первых, если ты планируешь открыть дверь в большой мир через несколько веб-сервисов, ты должен не забыть подобным образом защитить каждый из публичных методов. Кроме того, при таком подходе мы засоряем все публичные методы лишними данными и лишними программным кодом, тем самым давая увесистого пинка как производительности сервиса, так и простоте восприятия его исходников твоими друзьями.

Ладно, не буду больше терзать тебя догадками, а расскажу, как делают приватными свои сервисы серьезные господа. А делают они это с помощью самых что ни на есть подручных средств – мыла и веревки. Хотя нет, веревка нам сегодня не понадобится. Обойдемся одним мылом. Или как там оно будет на буржуйском международном? Правильно, SOAP. «И в чем же тут фишка?» - разочарованно спросишь ты, а я таки имею ответить! Всю информацию, используемую для аутентификации, мы уберем с глаз долой в заголовок SOAP-сообщения. Все публичные методы спрячем за гейткипером, который будет обрабатывать SOAP-заголовок и, в зависимости от того, понравился ему посетитель или нет, пропускать запрос внутрь нашей цитадели зла или посылать в аут, как это делал Овчинников с зенитовскими мячами в свои лучшие годы.

Пишем!

Итак, цель ясна и сформулирована - поднять веб-сервис, требующий от пользователя аутентификации с помощью логина и пароля. Проверка соответствия пароля эталонному образцу должна проходить до того, как управление будет передано основным методам сервиса. Приступим. Хотя нет, постой – еще пару слов в качестве напутствия. Очень важно, чтобы ты понимал, что цель этой статьи состоит в том, чтобы познакомить тебя с одним из аспектов защиты веб-сервисов. Приведенный пример не стоит рассматривать как готовое решение. Прежде всего это пища для размышления, которая должна подтолкнуть тебя к разработке серьезной комплексной защиты. Найди в этом предложении слово «комплексной», вырежи его и пришли мне, тебя ждет приз. Шутка – просто подчеркни его и запомни, так как комплексный подход – это альфа и омега любой защиты.

В качестве среды разработки мы будем использовать Visual Studio совместно с пакетом Microsoft WSE 3.0, который доступен для свободного скачивания по адресу www.microsoft.com/downloads.

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

C установкой WSE у тебя проблем возникнуть не должно. В результате, вызвав контекстное меню любого из проектов, ты увидишь новый пункт в самом низу списка, открывающий диалоговое окно настройки параметров WSE.

Запускаем Visual Studio и создаем новое решение, присвоив ему имя, ну скажем, ProximateService.

Щелчком правой кнопки мыши по только что созданному решению в окне Solution Exproler вызывай контекстное меню и добавляй новый веб-сайт. Кстати, я надеюсь, ты знаком с языком С#? Если нет – бегом восполнять пробел. Но сильно не напрягайся, если твои знания пока еще поверхностны. Нам понадобятся только самые основы. Всю грязную работу на себя берет WSE, проведя к заветной цели через цепочку визардов (понимаю твое возмущение, но «Вам шашечки или ехать?»). А если вдруг тебя всерьез заинтересует создание веб-сервисов, то учти, что на этом балу музыку заказывают два короля: C# и Java. Подружись с одним из них - и жизнь твоя будет в шоколаде. Не хуже, чем у Ксении Собчак. У нас есть решение, есть проект. Чего еще не хватает? Правильно, не хватает веб-сервиса. Назовем его ProxyListGetter.

В списке ресурсов сервиса прежде всего нужно переименовать созданный ASMX-файл в ProxyListGetterService.asmx. Теперь открываем его в окне редактора и меняем автоматически сгенерированный код на свой. В качестве отправной точки можешь использовать пример, приведенный на врезке. Подробно комментировать этот пример не имеет смысла, так как его исходный код примитивен и понятен даже тому, кто правой рукой за компилятор ни разу не держался. С другой стороны, если ты уже знаком с языком C# и используешь его для создания веб-сервисов, то ты наверняка заметил, что в приведенном примере, помимо импорта стандартных библиотек: System, System.Web и System.Web.Services, появилась еще одна библиотека – Microsoft.Web.Services. Она содержит, в частности, класс Policy, который нам предстоит позднее использовать для указания метода аутентификации.

Добавляем политику безопасности

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

Жмем правой кнопкой мыши по проекту в окне Solution Explorer и в контекстном меню выбираем пункт WSE Settings. На первой закладке открывшегося окна свойств нужно выбрать обе опции для того, чтобы разрешить использование WSE и внести соответствующие изменения в формат SOAP-сообщений. Далее переходим на закладку Policy. С помощью чекбокса активируем использование политики, после чего жмем кнопку Add/«Добавить» и вводим имя создаваемой политики безопасности.

Вот мы и добрались до горячо любимых тобой (на уровне подсознания, разумеется) визардов. На первой странице не задумываясь дави кнопку Next. На следующем шаге нужно выбрать сервис, к которому будет применена политика, и указать используемый метод аутентификации – нас пока интересует только Username – его и выбирай. После чего снова жми кнопку Next.

Теперь нужно убедиться в том, что расширение WS-Security активировано, а первая опция в секции Protection Order сброшена. Все, можно с чувством выполненного долга жать на кнопку Finish.

Еще не мешало бы проверить работоспособность системы аутентификации. Для этого на вкладке Diagnostics нужно активировать трассировку проекта. В процессе трассировки будут использоваться два файла – один для входящих запросов, а другой для исходящих ответов (согласись, очень удобная штуковина, позволяющая не только отлаживать работу своих сервисов, но и изучать все премудрости веб-программирования на примере сервисов, созданных другими разработчиками). В последний раз жмем ОК. Но это еще не все. Мы создали политику аутентификации, теперь нужно прикрутить ее к нашему сервису. Для этого опять переходим к окну редактора кода и к уже имеющимся атрибутам сервиса добавляем еще один – Policy:

[WebService(Namespace = "http://web.secrity.services/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

[Policy("ServerPolicy")]

Что изменилось? Смотри. Если теперь открыть файл web.config, то в нем ты найдешь секцию <microsoft.web.services3>, состоящую из ссылки на файл политики безопасности и указания пути, по которому будут располагаться файлы диагностики.

Фрагмент файла web.config

<microsoft.web.services3>

<policy fileName = "wse3policyCache.config" />

<diagnostics>

<trace enabled = "true" input = "InputTrace.webinfo" output = "OutputTrace.webinfo"/>

</diagnostics>

</microsoft.web.services3>

Теперь не мешало бы разобраться с самим файлом политики безопасности. В нем присутствует раздел ServerPolicy. Именно из этого раздела сервис и узнает, что, прежде чем покорно отдать пользователю заветный список приватных проксиков, у этого самого пользователя не мешало бы поинтересоваться, кто он и с какой целью пожаловал. Если переходить на термины информационной безопасности, то созданная нами система защиты – это пока даже не аутентификация, а идентификация. Еще хотелось бы обратить внимание на тот момент, что мы не использовали шифрование. Сделано это опять-таки осознанно – чтобы не уходить от основной темы статьи. Это, однако, вовсе не означает, что приватная информация будет передаваться в открытом виде, ни в коем случае. Просто этот аспект защиты мы переносим на транспортный уровень, шифруя весь трафик с помощью того же SSL. Если тебе этого недостаточно и ты знаком с основными криптоалгоритмами, написать их реализацию на C# для тебя труда не составит.

В общем случае содержимое такого файла будет выглядеть следующим образом:

<policy name = "ServerPolicy">

<usernameOverTransportSecurity />

<requireActionHeader />

</policy>

 

Совершенствуем защитный механизм

Вот так незаметно мы перевалили за середину статьи, а надежной защиты веб-сервиса у нас по-прежнему нет. Почему? Да потому, что, добавив механизм аутентификации, мы закрыли доступ к сервису только анонимным пользователям. Это означает, что любому желающему заюзать наш приватный список прокси-серверов достаточно подключиться к сервису, использовав любой логин, какой только может выдумать его воспаленный ожиданием легкой наживы мозг. Поэтому следующая наша задача – обеспечить доступ к сервису только избранным, а остальных послать туда, куда не доходят ICMP-пакеты. Для этого, во-первых, нужно создать класс, являющийся наследником класса Microsoft.Web.Services3.Security.TokensUserNameTokenManager. Во-вторых, этот класс нужно зарегистрировать в качестве ресурса веб-сервиса. Следовательно, добавляем к решению новый проект. Назовем его SecurityHelper. Тип создаваемого класса – библиотечный (library). Переименовываем класс в нечто соответствующее общему контексту, например в CustomUserTokenManager.cs, и меняем шаблон исходного кода этого класса на код, приведенный во врезке.

Как видишь, исходник опять достаточно очевиден и в особых комментариях не нуждается. Отдельно, пожалуй, следует обратить внимание на то, что в самом начале мы объявляем использование нестандартной библиотеки: using Microsoft.Web.Services3.Security.Tokens. Эта библиотека как раз и содержит в себе класс, являющийся предком CustomUserTokenManager.cs.

Теперь подскажем веб-сервису, что впредь при обработке поступающих запросов ему нужно руководствоваться усовершенствованным механизмом аутентификации. Для этого необходимо добавить секцию security в файл web.config, что можно сделать двумя способами: либо воспользоваться очередным мастером, либо написать соответствующий код своими руками. Давай пойдем вторым путем, ты ведь знаешь, сколько ненужной шелухи остается после работы генераторов кода на основе шаблонов.

Добавляем новый модуль в файл web.config

<microsoft.web.services3>

<policy fileName="wse3policyCache.config /">

<security>

<securityTokenManager>

<add type="web.secrity.services.CustomUsernameTokenManager, SecurityHelper" namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"

localName="UsernameToken" />

</securityTokenManager>

</security>

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

Клиентские разборки

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

using Microsoft.Web.Services3.Security.Tokens;

Прежде чем передать веб-сервису запрос, содержащий в себе параметры аутентификации пользователя, их необходимо соответствующим образом подготовить. Делается это через объект UsernameToken:

UsernameToken token = new UsernameToken(user, password, PasswordOption.SendPlainText);

Обращение клиента к веб-сервису начинается также с создания соответствующего объекта:

ProximateService.ProxyListGetterService proxy = new ProximateService.ProxyListGetterService();

После этого передаем ему в качестве параметра функции SetClientCredential объект UsernameToken. Так как пакет WSE предусматривает возможность создания в рамках одного веб-сервиса нескольких политик безопасности, при формировании запроса необходимо указать имя той политики, которой будут обрабатываться передаваемые параметры:

proxy.SetPolicy("ClientPolicy");

Дальше могут идти стандартные запросы к интерфейсам веб-сервиса – все как обычно. Если с созданием клиента у тебя вдруг возникнут трудности, можешь посмотреть и взять за основу исходники, выложенные на диске.

Код веб-сервиса

using System;

using System.Web;

using System.Web.Services;

using Microsoft.Web.Services;

[WebService(Namespace = "http://web.secrity.services/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

public class ProxyListGetterService : System.Web.Services.WebService

{

public ProxyListGetterService()

{

}

[WebMethod]

public string GetProxyList(string country)

{

string ProxyList;

switch (country.ToLower()){

{

case "ru":

ProxyList = "151.204.42.140:8080";

break;

case "us":

ProxyList = "62.4.85.203:3128";

break;

case "ch":

ProxyList = "141.108.9.105:8080";

break;

default:

ProxyList = "213.170.40.76:80"

break;

}

return ProxyList;

}

}

Проверка пользовательских данных

using System;

using System.Xml;

using Microsoft.Web.Services3.Security.Tokens;

namespace web.secrity.services

{

public class CustomUsernameTokenManager : UsernameTokenManager

{

public CustomUserNameTokenManager()

{

}

public CustomUsernameTokenManager(XmlNodeList nodes)

: base(nodes)

{

}

protected override string AuthenticateToken(UsernameToken token)

{

string password;

switch (token.Username.ToLower())

{

case "nab":

password = "wse3";

break;

dafault:

password = "!" + token.Password;

break;

}

return password;

}

}

}

DVD

На диске лежат исходные коды веб-сервиса и клиента, для сборки которых тебе понадобится Microsoft Visual Studio с установленным пакетом WSE.

Самую свежую версию Microsoft WSE также ищи на нашем диске.

WWW

Веб-сервисы далеко не ограничиваются технологиями Microsoft. Для того чтобы убедиться в этом, посети сайт www.ibm.com/developerworks/ru.

INFO

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

Содержание