Запросы и ответы HTTP — Примеры

Пример 1 — Повтор, создающий второй заказ

Клиент сообщает, что создание иногда приходит дважды. Обмен ниже — тот, что предшествовал этому. Скажите, какая инструкция опасна, почему метод сделал этот повтор небезопасным и что изменить.

запрос
  POST /orders
  Host: адрес магазина
  Content-Type: application/json
  Content-Length: 84
ответ
  301
  Location: тот же узел и путь, защищённая схема
  Cache-Control: max-age=31536000

Решение

причина:  301 кэшируется и запоминается, поэтому каждый следующий запрос
          повторяется против защищённой схемы; POST не безопасно повторять,
          и повтор может создать дубликат
изменить: используйте 302 для условия, которое может измениться; оставляйте 301
          только для постоянного переноса
          помечайте ответы создающего эндпоинта как некэшируемые
сохранить: GET по-прежнему повторяется свободно; PUT /orders/42 безопасно повторять

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

Пример 2 — Заголовок пересылки, который выбрал вызывающий

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

как видит приложение
  GET /profile
  Host: внутреннее имя приложения
  X-Forwarded-For: 198.51.100.7, 10.0.0.5

Решение

на прокси: перезаписывайте X-Forwarded-For адресом, который он наблюдал, никогда
           не дописывайте недоверенный ввод и отбрасывайте заголовок у запросов
           извне
в приложении: читайте значение только из доверенного прокси, иначе используйте
           адрес узла самого соединения
также:     аутентифицированный ответ профиля помечен Cache-Control: private

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