xmlhack.ru logo
>> статьи на xmlhack.ru

httplib2: постоянные соединения и идентификация пользователя в протоколе HTTP

Автор: Джо Грегорио
Перевод: Кондаков Валерий
Опубликовано на XML.com (29.03.2006, англ.): http://www.xml.com/pub/a/2006/03/29/httplib2-http-persistence-and-authentication.html
Опубликовано на xmlhack.ru (25.04.2006, рус.): http://xmlhack.ru/texts/06/http-persistence-and-auth/http-persistence-and-auth.html
В закладки:   Del.icio.us   reddit

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

Постоянные соединения

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

Таким образом, если все соединения по протоколу HTTP 1.1 решено делать постоянными, то должен существовать механизм распознавания, должно ли соединение быть закрытым, не так ли? Этот механизм управляется заголовком Connection:.

  Connection: close

Заголовок сигнализирует, что соединение должно быть закрыто после того, как текущий запрос/ответ завершён. Заметьте, что как клиент, так и сервер могут посылать такой заголовок.

Если вы допускаете постоянные соединения, тогда следующей очевидной оптимизацией является конвейеризация: отправка пачки запросов в сокет без ожидания ответа на первый запрос до отправки последующих запросов. Это срабатывает только для определённого вида запросов; как минимум, такие запросы должны быть идемпотентными. Разве не прекрасно, что вы сделали все свои методы GET идемпотентными, когда разрабатывали свой веб-сервис на основе REST?

Сжатие

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

Хотя RFC 2616 предписывает три типа компрессии, значения кодов описаны в IANA registry и теоретически могут быть дополнены другими. Однако, прошло девять лет с тех пор, как был опубликован протокол HTTP 1.1, а список значений так и не пополнялся. И даже хотя предписаны три типа компрессии, только два из них, gzip и compress, регулярно наблюдаются в данной среде.

Сжатие обычно используется следующим образом: клиент объявляет типы сжатия, которые он поддерживает, используя заголовок запроса Accept-Encoding:

  Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0

Это взвешенные параметры с правилами резолюции, похожими на MIME-типы в заголовках Accept:. Я рассмотрел их парсинг и интерпретацию в статье «Просто использовать media-типы?» (“Just Use Media Types?”), которую следует прочесть, если вы ещё этого не сделали.

Если сервер поддерживает любой из перечисленных типов сжатия, он может заархивировать ответ и объявить, какой был использован тип сжатия, чтобы клиент мог правильно его разархивировать. Эта информация переносится в заголовке Content-Encoding:.

  Content-Encoding: gzip

В процессе реализации httplib2 я также рассказал о некоторых узких местах в реализациях протокола HTTP.

Идентификация

Раньше люди спрашивали меня, как защитить их веб-сервисы, и я советовал им просто использовать идентификацию протокола HTTP, под которой я подразумевал основную (Basic) или профильную (Digest) идентификацию, как описано в RFC 2617.

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

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

Вот «основные положения» профильной идентификации протокола HTTP:

  1. Сервер отклоняет неидентифицированный запрос с помощью челленджа. Этот челлендж содержит слово, образованное лишь для данного случая, случайную строку, сгенерированную сервером (nonce, «текучку»).
  2. Клиент отвечает тем же запросом, но на этот раз с заголовком WWW-Authenticate:, который содержит хэш объявленной «текучки», имя пользователя, пароль, запрашиваемый URI, и метод протокола HTTP.

Проблема с профильной идентификацией в том, что она зависит от слишком большого числа возможностей, которые реализуются неодинаково и не всегда правильно. Например, есть возможность использовать тело сущности при вычислении хэша, т.н. auth-int. Также существует два различных вида хэширования, MD5 и MD5-sess. Сервер может возвращать новую «текучку» челленджа с каждым запросом, или клиент может включать монотонно возрастающее с каждым запросом значение, вычисляемое на основании «текучки». Сервер так же имеет возможность возвращения собственного профиля, с помощью которого сервер может доказать клиенту, что ему известен их общий секретный ключ.

Учитывая все эти возможности, неудивительно существование проблем с взаимодействием. Например, Apache 2.0 не поддерживает auth-int в профильной идентификации. В то время, как urllib2 на языке Python требует MD5-sess, Apache не реализует его правильно. В дополнение, разбирая код urllib2 на языке Python, оказывается, что библиотека поддерживает хэш SHA в дополнение к стандартному хэшу MD5. Единственная проблема в том, что нет ни единого намёка на хэш SHA в числе возможностей, упомянутых в RFC 2617. И, конечно же, никакого намёка на профильную идентификацию, кроме как в браузере Internet Explorer, который не вычисляет профиль должным образом для тех URI, в которых наличествуют параметры запросов.

Итак, хотя начинает казаться, что мы загнаны в ловушку извращённого скетча группы Monty Python, всё же существуют отдушины: в Apache версии 2.0.51 и поздних версий вы можете заставить IE работать с профильной идентификацией, используя следующую директиву:

BrowserMatch "MSIE" AuthDigestEnableQueryStringHack=On

Замечательно, теперь вы знаете, что попали в беду, если директива AuthDigestEnableQueryStringHack — ваше спасение.

О да, последний трюк с реализацией как основной, так и профильной идентификации состоит в том, что вы можете сохранять URI, которые вы идентифицировали, поскольку, если потребуется получить доступ к URI, находящемуся «под» идентифицированным URI, вы можете послать идентификацию первого запроса и не ждать челленджа. Говоря, что URI находится «под» другим URI, я подразумеваю что он «основывается на пути этого второго URI». Кроме того, знайте, что идентификация на нижнем уровне пути может потребовать иного набора удостоверений или использовать иную схему идентификации.

Если вы решите выйти за пределы RFC 2617, вам следует использовать WSSE, однако, этот подход не специфичен для «чистого» HTTP; и такой подход не работает ни на одном из известных браузеров, он был разработан для WS-Security и неофициально портирован для работы в заголовках протокола HTTP, а не в рамках SOAP; объяснение можно найти в статье XML.com, и, поскольку статья XML.com была опубликована в августе, этого нет ни в IETF, ни в W3C.

Итак, вы можете подумать, что я могу использовать TLS (HTTPS), который также используется в большинстве веб-приложений и сервисов в связке с основной идентификацией протокола HTTP. Однако, следует понимать, что я, как и многие другие, использую разделяемый хостинг; даже если бы я достал деньги на покупку сертификата, я бы не смог настроить TLS для моего сайта, поскольку сертификаты связываются со специфическими IP-адресами, а не с доменным именем. Это на самом деле плохо, поскольку клиентская поддержка TLS (HTTPS) кажется довольно неплохой.

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

Для дальнейшего изучения следует прочесть заметку W3C от 1999 (!), «Формы идентификации агентов пользователя» (“User Agent Authentication Forms”). В дополнение к тому спецификация “Web Applications 1.0 specification” в WHATWG включает как требование «Определённую лучшим образом обработку состояния идентификации пользователя. (Возможность достоверно «отключиться» от сайта, к примеру, или возможность интегрировать модель идентификации протокола HTTP в веб-страницу.)»

Перенаправления

Когда я реализовывал перенаправления 3xx, я натолкнулся на пару новых для меня моментов, некоторые из которых могут улучшить производительность. Итак, по сути, 3xx-серии статусных кодов протокола HTTP служат либо для перенаправления клиента на новый адрес, либо для указания, что клиент должен проделать ещё некоторый объём работы.

В частности, я усвоил, что 300, 301, 302, и 307 могут кэшироваться при некоторых обстоятельствах: либо по умолчанию, либо при использовании заголовков управления кэшированием. Это означает, что в случае, когда клиент реализует кэширование, он может избежать одной или более итераций, если он способен хэшировать эти 3xx ответы.

httplib2

В конце моей последней статьи я представил httplib2, библиотеку для языка Python, которая реализует все возможности кэширования, рассмотренные в статье. Таким образом, httplib2 предоставляет многие из таких возможностей, как HTTPS, Keep-Alive, Basic, Digest, WSSE, а также обе ( gzip и compress) формы сжатия. На сей раз достаточно библиотек и спецификаций; в следующей статье мы вернёмся к написанию кода и внедрению всей этой инфраструктуры в работу.



XML.com Copyright © 1998-2007 O'Reilly Media, Inc.
Перевод: xmlhack.ru Copyright © 2000-2007 xmlhack.ru