На главную
От редакции

Приведенные ниже тривиальные факты о правильной работе с протоколом HTTP не есть паранаучный текст и, тем более, рекламный пост, как его обозначил Капитан Очевидность. Это - нормальное справочное пособие для айтишников. Но Капитан Очевидность написал его так легко и так весело, что редакция ТЧК не прошла мимо этой публикации, и предлагает автору дальнейшее сотрудничество в публикации подобных текстов.

e-lub
2 Sep 15

УДК 004.04

Аннотация
Капитан Очевидность. 15 ТРИВИАЛЬНЫХ ФАКТОВ О ПРАВИЛЬНОЙ РАБОТЕ С ПРОТОКОЛОМ HTTP

Ниже вы найдёте 15 пунктов, описывающих правильную организацию ресурсов, доступных по протоколу HTTP - веб-сайтов, ручек бэкенда, API и прочая. Правильный здесь означает соответствующий рекомендациям и спецификациям. Большая часть ниженаписанного почти дословно переведена из официальных стандартов, рекомендаций и best practices от IETF и W3C

Вы не найдёте здесь абсолютно ничего неочевидного. Нет, серьёзно, каждый веб-разработчик теоретически эти 15 пунктов должен освоить где-то в районе junior developerа и/или второго-третьего курса университета.

Однако на практике оказывается, что великое множество веб-разработчиков эти азы таки не усвоило. Читаешь документацию к иным API и рыдаешь. Уверен, что каждый читатель таки найдёт в этом списке что-то новое для себя.




1. URL идентифицирует ресурс - некоторую разделяемую сущность. Файл - ресурс. Ручка, которая что-то ищет - ресурс. Вызов метода - не ресурс. Если вы хотите шарахнуть из пушки по Лунe, то вот так делать не надо:

GET /?method=шарахнуть&to=Луна

Заведите ресурс шарахалка, и тогда у вас всё будет логично:

POST /шарахалка/?to=Луна

Почему POST, а не GET? Читай ниже.

2. URL состоит из схемы, хоста, пути, запроса и фрагмента. Путь используется для организации иерархических ресурсов, запрос - для неиерархических ресурсов и для параметров операции. Фрагмент идентифицирует подчинённый ресурс, не имеющий прямого URL.
Scheme Host Path Query Fragment
http://nyashnye-kotiki.xxx/breeds/maine-coon/?deliver_to=Moscow#photo

Если на вашем сайте Няшные котики есть каталог по породам, то его вполне логично организовать в виде частей path, поскольку каждый котик принадлежит ровно к одной породе. А вот доставлять одного котика можно в несколько городов, поэтому фильтр с доставкой в город N следует организовать через query.

3. Обращение по HTTP состоит из применения метода к URL. Результатом такого применения должно быть - сюрприз-сюрприз! - то, что в глаголе написано. То есть GET возвращает представление ресурса, DELETE удаляет и т.п.

4. Методы GET, HEAD, OPTIONS - безопасные. Предполагается, что вызов этих методов состояния ресурса не изменяет. Поэтому многие сетевые агенты - такие, например, как префетчер ссылок в браузере или мессенджере - считают себя вправе по таким ссылкам ходить без явного волеизъявления пользователя. ИЧСХ, никаких стандартов не нарушают.

5. По умолчанию методы GET и HEAD кэшируются, OPTIONS, POST, PUT, PATCH, DELETE - нет. Поэтому если вы шарахнули по Луне методом POST, вы можете быть уверены, что этот запрос выполнится. Если вы шарахаете методом GET, какой-нибудь промежуточный прокси может внезапно отдать вам ответ из кэша, и шарах в реальности не произойдёт.

6. Операции GET, PUT, DELETE симметричны. PUT кладёт нечто по URLу, GET по этому URLу возвращает представление того, что положил PUT, DELETE удаляет ресурс. Метод HEAD синонимичен по семантике методу GET, но не возвращает тело ответа, а только его заголовки.

7. POST используется в том случае, если у вас нет URL, к которому вы хотите применить операцию. Например, если пользователь пишет новое сообщение в тредик на форуме, он может сам вычислить его id и сделать:

PUT /threads/php-rulezz/messages/100500

Если клиенту генерировать id не разрешено, ему придётся делать POST на ресурс уровнем выше по иерархии:

POST /threads/php-rulezz/messages

И этот ресурс сам создаст новое сообщение.

Обратите внимание, если вы по ошибке или вследствие сетевых проблем повторите POST запрос - создастся второе сообщение в треде, идентичное первому. PUT вы можете делать хоть 100500 раз, результат не изменится. Это свойство называется идемпотентностью.

Ладно создание постов на форуме. Вот если вы делаете тяжёлую и дорогую операцию по пользовательскому запросу - очень рекомендуется выполнять для этого идемпотентный запрос. А то может получиться как на картинке:

Ладно создание постов на форуме. Вот если вы делаете тяжёлую и дорогую операцию по пользовательскому запросу - очень рекомендуется выполнять для этого идемпотентный запрос. А то может получиться как на картинке:

Разумеется, использование идемпотентного PUT порождает свои проблемы - в частности, как разрешать конфликты. Придётся больше программировать, зато результат будет более надёжным и безопасным.

8. PUT может использоваться как для создания новых ресурсов, так и для обновления старых. Однако в случае использования PUT для перезаписи предполагается, что в теле запроса передаётся закодированный ресурс целиком. Если же вы хотите модифицировать ресурс, т.е. изменить его внутреннее представление без полной перезаписи, то для этого был придуман метод PATCH. Этот метод некэшируемый, небезопасный и неидемпотентный.

9. Коды ответа нужны в первую очередь для того, чтобы клиент мог понять, что ему делать дальше.

3хх говорит, что для успешного выполнения запроса нужно выполнить дополнительное действие.

4хх говорит, что клиент, составляя запрос, сделал что-то неправильно и, обычно, о том, что умолять бесполезно - повторное выполнение запроса всё равно выкинет ошибку. В 4хх крайне рекомендуется включать информацию о том, что конкретно клиент сделал не так.

5хх говорит о том, что клиент всё сделал правильно - проблема на стороне сервера.

Обычно при успешном выполнении операции сервер отвечает на GET - 200, на PUT - 201 Created или 200, на DELETE - 204, на POST - 200 или 201.

10. Работая с HTTP-статусами, не наступите на популярные грабли:

✓статус 401 Unauthorized обязан сопровождаться заголовком WWW-Authenticate и, таким образом, применим только тогда, когда клиент аутентифицируется посредством HTTP-аутентификации; во всех остальных случаях следует использовать 403 Forbidden;

✓статусы 3xx - это не только редиректы; они показывают, что клиент должен выполнить дополнительное действие, иначе запрос не может считаться успешным; например, по статусу 304 Not Modified клиент должен взять актуальную версию ресурса из кэша;

✓статус 404, как ни странно, один из немногих 4xx статусов, которые клиент имеет право повторять - он означает, что ресурса сейчас нет, но вполне возможно, что он появится; вообще 404 - статус неопределённости, который используется, если сервер не хочет раскрывать механику ошибки; для того, чтобы индицировать клиенту, что без дополнительных действий с его стороны ресурс не появится, следует использовать 410 Gone либо общий статус 400.

11. Существует особый подкласс URLов, которые кодируют в себе и ресурс, и действие над ним. В англоязычной литературе их принято называть Capability URLs. Классический пример такого URL - ссылки на восстановление паролей, а также всевозможные «секретные» прямые ссылки на всяческие ресурсы.

12. Поскольку основная опасность при работе с Capability URL - возможность их утечки, следует максимально закрыть возможности случайно такой URL найти или перехватить:

✓для генерации секретных частей URL должен использоваться сильный генератор случайных строк, исключающий возможности найти Capability URL перебором; разумеется, URL не должен генерироваться детерминированным способом типа md5 и такие URL нельзя пропускать через сокращатели ссылок;

Capability URLs должны работать только по HTTPS;

✓страницы, доступные через Capability URL, должны быть закрыты wildcardом от индексации роботами.

13. Должны быть предусмотрены меры минимизации возможного ущерба: пользователь, создавший Capability URL, должен иметь возможность сделать обратную операцию, т.е. отозвать URL;

Capability URLs должны протухать со временем; чем опаснее предоставляемый доступ, тем короче должен быть срок жизни URL.

14. Наконец, сами «секретные» страницы должны быть защищены от сливания данных сторонним агентам:

✓на них не должно быть никаких third-party скриптов и картинок, желательно - на уровне CSP;

✓на них не должно быть ссылок на third-party сайты; если они необходимы, то нужно скрывать referrer, например, через rel="noreferrer";

✓вообще желательно через Referrer Policy настроить скрытие referrerа;

✓желательно сразу после захода пользователя через History API менять URL в адресной строке браузера, чтобы его нельзя было подсмотреть через плечо;

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

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

Да плевать я на всё это хотел, придумали какой-то ненужной ерунды; как у меня работали все сервисы только на GET, так и дальше будут, мучайтесь со своими PUTами и DELETEами сами.

Разумеется, вы вольны писать свой сервис сами. Но имейте, пожалуйста, в виду, что между вашим сервером и вашим клиентом, даже если они стоят физически рядышком в одном ДЦ, есть огромное множество других сетевых агентов - браузеров, прокси, роутеров, имплементаций HTTP-протокола в разных языках программирования и разных ОС, DPI-оборудование провайдеров и так далее. Все эти агенты плюс-минус имплементируют протокол HTTP с оглядкой на RFC.

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

Но даже не это главное. Допустим, ваши HTTP-пакеты гуляют в строго контролируемой среде. Как вы собираетесь объяснять другим разработчикам, какие рекомендации вы нарушили и почему? Как ваш коллега должен понять, что вот этот GET-запрос повторять нельзя, а статус 400 вовсе не означает клиентскую ошибку? Отступая от рекомендаций, вы, фактически, каждый раз создаёте какой-то свой диалект HTTP с собственной семантикой. Не забудьте его хотя бы задокументировать ;)

К началу
Список литературы

1. www.rfc-editor.org/rfc/rfc2616.txt.

2. tools.ietf.org/html/rfc5789.

3. www.w3.org/TR/webarch.

4. w3ctag.github.io/capability-urls. В разработке последнего документа ваш покорный слуга принимал определённое участие.

Приглашение к обсуждению прочитанного

Из wikipedia.org

Свободная энциклопедия

HTTP, протокол прикладного уровня передачи данных.

К тексту

Back end are distinctions which refer to the separation of concerns between a presentation layer and a data access layer respectively..

К тексту

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

К тексту

Инженерный совет Интернета

Инженерный совет Интернета, открытое международное сообщество проектировщиков, учёных, сетевых операторов и провайдеров, созданное IAB в 1986 году и занимающееся развитием протоколов и архитектуры Интернета.

К тексту

Консорциум Всемирной паутины

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

К тексту

URL, единообразный локатор ресурса.

К тексту

Prefetcher is a component of Microsoft Windows which was introduced in Windows XP.

К тексту

Браузер

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

К тексту

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

К тексту

Тред, последовательность ответов на сообщение, то есть «ветвь обсуждения».

К тексту

CSRF-токен, вид атак на посетителей веб-сайтов, использующий недостатки протокола HTTP.

К тексту

Дата-центр

Дата-центр, специализированное здание для размещения (хостинга) серверного и сетевого оборудования и подключения абонентов к каналам сети Интернет.

К тексту

Операционная система, комплекс взаимосвязанных программ, предназначенных для управления ресурсами вычислительного устройства и организации взаимодействия с пользователем.

К тексту

Deep packet inspection is a form of computer network packet filtering that examines the data part of a packet as it passes an inspection point, searching for protocol non-compliance, viruses, spam, intrusions, or defined criteria to decide whether the packet may pass or if it needs to be routed to a different destination, or, for the purpose of collecting statistical information.

К тексту

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

К тексту

"Королю спама" грозит тюремный срок

Одному жителю США, ставшему известным, как «король спама», может грозить тюремный срок и крупный штраф за массовую рассылку 27 миллионов нежелательных сообщений, используя сеть Facebook.

47-летний Сэнфорд Уоллес (Sanford Wallace) получил доступ к 500 тысячам учётных записей пользователей социальной сети обманными путями, после чего использовал учётные записи своих жертв для рассылки спам сообщений их друзьям.

Те, кто проходили по ссылке, которую отправлял Уоллес с чужих учётных записей, попадали на один из сайтов, которые платили ему деньги за привлечение посетителей.

Уоллес был арестован в этом году в ходе расследования кражи личной информации, и признался, что с 2008 по 2009 год отправил со взломанных учётных записей около 27 миллионов сообщений. Также суд узнал, что Уоллес смог получить доступ к компьютерной сети Facebook противозаконным путём. Напоминает тот случай, когда мошенники заработали 6 миллионов долларов за ремонт компьютера пианиста.

7 декабря 2016 года ему будет вынесен приговор. Ему грозит до трёх лет лишения свободы и штраф размером в 250 тысяч долларов. Также в 2008 году он проиграл суд против компании Myspace за то же нарушение, и должен был вместе со своим партнёром выплатить компании более 200 миллионов долларов в качестве компенсации, чего так и не сделал.

От редакции

Редакция ТЧК постарается отследить ход процесса и сообщить об этом своим читателям