Почему GET-запрос не должен изменять данные на сервере?

109

По всему интернету я вижу следующий совет:

GET никогда не должен изменять данные на сервере - используйте для этого запрос POST

Что лежит в основе этой идеи?

Если я создаю php-сервис, который вставляет данные в базу данных и передает их параметры в строку запроса GET, почему это неправильно? (Я использую подготовленные операторы, чтобы заботиться о SQL-инъекции). Является ли POST-запрос более безопасным?

Или есть какая-то историческая причина для этого? Если да, насколько актуален этот совет сегодня?

Девдатта Тенгше
источник
Спасибо, что задали этот вопрос, и спасибо @Oded за хорошо сформулированный ответ. Мне всегда требовалась ссылка, чтобы послать людей, которые задают этот вопрос, к :)
Бенджамин Грюнбаум
Также см. HTTP PUT - stackoverflow.com/questions/630453/put-vs-post-in-rest (с примечаниями о том, что он идемпотентен)
Bratch
2
@JoachimSauer Хотя GET спас бы их от искателя, основной проблемой было отсутствие аутентификации. Любой сценарист мог бы забыть о них.
CodesInChaos

Ответы:

185

Это не совет.

A GETопределяется таким образом в протоколе HTTP . Он должен быть идемпотентным и безопасным .

Что касается того, почему - GETможно кэшировать и обновлять в браузере. Снова и снова и снова.

Это означает , что если вы сделаете то же самое GETеще раз, вы будете вставлять в вашу базу данных снова .

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

Я также предлагаю читать URI, адресуемость и использование HTTP GET и POST .


Существует также проблема с предварительной загрузкой ссылок в некоторых браузерах - они будут вызывать ссылки предварительной выборки, даже если автор страницы не указал это.

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

Одед
источник
35
Многие, многие, многие инструменты, утилиты, сканеры веб-сайтов и другие вещи считают, что GETэто никогда не будет разрушительным действием (справедливо, поскольку это определено так). Если вы теперь нарушите свое приложение, нарушив эту спецификацию, вы сохраните обе части своего приложения.
Йоахим Зауэр
7
@NimChimpsky: он действительно меняется GET. Этот совет просто неверен. Безопасный означает, что пользователь не может нести ответственность за побочные эффекты, а также что побочные эффекты не могут быть. В противном случае вы не могли бы иметь файлы журнала для вашего сервера, что было бы абсурдом! Это четко изложено в разделе 9.1.1 RFC2616.
Йорг Миттаг
8
@ JörgWMittag: Я бы не сказал «просто неправильно», я бы сказал «неправильно сформулировано». У GET не должно быть изменений, так как это цель. Конечно, вы можете считать, регистрировать и наблюдать запрос GET. Но это не должно изменять ваши фактические бизнес-данные.
Йоахим Зауэр
23
@NimChimpsky A GETне должен изменять ресурс, запрашиваемый объектом GET, но это не означает, что «ничего на сервере не должно меняться». Конечно, такие вещи, как журналы, счетчики и другое состояние сервера могут меняться во время любого запроса.
Эрик Кинг
8
Несколько лет назад Google выпустил надстройку для браузера (iirc), которая будет предварительно извлекать страницы по ссылкам. Это также происходило на некоторых панелях управления, которые были спроектированы плохо - URL-адреса могли привести к тому, что запись или что-то было записано или даже удалено на сервере (подумайте post? Action = delete). Это привело к выполнению действий без ведома пользователя. Google прекратил эту надстройку по этой причине, iirc, даже если это была ошибка производителя веб-приложений за использование GET для изменения состояния.
Ктулху
24

Каждый HTTP-глагол имеет свою ответственность. Например GET, как определено RFC

означает извлечение любой информации (в форме объекта), идентифицируемой посредством Request-URI.

POSTс другой стороны, означает вставить или более формально

Метод POST используется для запроса, чтобы исходный сервер принял
объект, включенный в запрос, в качестве нового подчиненного ресурса,
идентифицируемого Request-URI в строке запроса

Причины для сохранения этого пути:

  • Это очень просто и работает в глобальном масштабе Интернет с 1991 года
  • Придерживайтесь принципа единой ответственности
  • Другие стороны используют GETв качестве средства поиска информации и добычи данных
  • Предполагается, что GET является безопасной операцией, которая никогда не изменяет состояние ресурса.
  • Соображения безопасности GET- это, по сути, чтение , тогда как, POSTпо сути, запись
  • GET кэшируется браузерами, узлами в сети, интернет-провайдерами
  • Если содержимое не изменяется, один GETи тот же URL-адрес должен возвращать одинаковые результаты всем пользователям, иначе вы не будете доверять возвращаемому результату.

Для полноты и просто для обеспечения правильного использования (источник) :

  • GETпараметры передаются как часть URL, который по умолчанию имеет небольшую и ограниченную длину в 256 символов, при этом некоторые серверы поддерживают более 4000 символов. Если вы хотите вставить длинную запись, нет законного способа передать эти данные в
  • При использовании защищенного соединения ̶ , такие , как Tls, ̶ Ссылка не получает зашифрованы, ̶ Поэтому все параметры ̶ ̶G̶E̶T̶̶ передаются открытым текстом. URL-адрес фактически зашифрован с помощью TLS, поэтому с TLS все в порядке.
  • Вставка двоичных данных или символов, отличных от ASCII GET, нецелесообразна
  • GET повторяется, если пользователь нажимает кнопку «Назад» в браузере
  • Некоторые старые сканеры могут не индексировать URL-адреса со ?знаком внутри
алексей
источник
1
Вы уверены, что URL не зашифрован по TLS? У меня сложилось впечатление, что рукопожатия SSL / TLS происходят до передачи заголовков HTTP. Это причина, по которой виртуальный хостинг HTTPS-сайтов по одному IP-адресу затруднен. Я ошибаюсь?
Брэндон
Правильно, я это исправил
Алексей
2
@Brandon Современные браузеры отправляют домен хоста в открытом виде как часть рукопожатия TLS (известного как указание имени сервера), чтобы разрешить размещение более одного домена на один IP-адрес. Часть пути / запроса URL-адреса защищена TLS. В этом отношении нет разницы между GET и другими HTTP-глаголами.
CodesInChaos
9

РЕДАКТИРОВАТЬ: Ранее я сказал, что POST помогает защитить вас от CSRF, но это неправильно. Я не продумал это правильно. Вы должны требовать уникальный скрытый маркер области сеанса во всех ваших запросах, чтобы изменить данные для защиты от CSRF.

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

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

Сарел Бота
источник
1
CSRF одинаково возможен с GET и POST. Например, злоумышленник может разместить на своем сайте форму автоматической отправки для запуска запроса POST. Стандартный подход к предотвращению CSRF явно включает в запрос значение, неизвестное злоумышленнику (в отличие от неявного включения заголовков cookie).
CodesInChaos
8

Если я создаю php-сервис, который вставляет данные в базу данных и передает их параметры в строку запроса GET, почему это неправильно?

Самый простой ответ - «потому что это не GETзначит».

Использование GETдля передачи данных для обновления похоже на написание любовного письма и отправку его в конверте с пометкой "СПЕЦИАЛЬНОЕ ПРЕДЛОЖЕНИЕ - ДЕЙСТВУЙ!" В обоих случаях вы не должны удивляться, что получатель и / или посредники неправильно обрабатывают ваше сообщение .

Натан Лонг
источник
5

Для ваших операций CRUD в приложении, ориентированном на базу данных, используйте следующую схему:

Использовать HTTP GET для операций чтения (SQL SELECT)

Использовать HTTP PUT для операций обновления (SQL UPDATE)

Использовать HTTP POST для операций создания (SQL INSERT)

Использовать HTTP DELETE для операций удаления (SQL DELETE)


источник
3
Положите против сообщения не так, как вы утверждаете. Put - это когда клиент изменяет ресурс в указанном месте. Для поста сервер в конечном итоге решает точный Uri ресурса.
Энди
Разве HTTP PUT больше не похож на SQL DELETE и INSERT, а не на UPDATE? Также SQL UPDATE может обновлять много записей одновременно, но HTTP PUT обновит только одну вещь.
Backwards_Dave
0

GET никогда не должен изменять данные на сервере - используйте для этого запрос POST

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

GET редко должен изменять данные на сервере - используйте для этого запрос POST

Сказать «никогда» не является слишком экстремальным, и хотя другие ответы здесь точно объясняют, почему вы должны «редко» делать это, есть некоторые сценарии, когда совершенно разумно изменять данные с помощью GET. Примером является одноразовая ссылка для проверки электронной почты. Обычно эти ссылки содержат GUID, который при обращении должен будет изменить данные. При правильной реализации последующие идентичные запросы GET будут игнорироваться.

Это, очевидно, крайний случай, но, безусловно, стоит отметить.

ТТТ
источник
3
Что если ваш почтовый клиент решит получить ссылку, не нажимая на нее? Например, потому что он хочет сканировать его на наличие вредоносных программ. Надлежащий подход к ссылкам для отмены подписки заключается в том, чтобы перейти на страницу, где пользователь может нажать кнопку, чтобы отменить подписку (где нажатие кнопки вызывает запрос POST).
CodesInChaos
@CodesInChaos - отличный момент! Я согласен. Я удалил пример отказа от подписки и оставил подтверждение электронной почты в качестве единственного примера. Помимо проверки электронной почты, могут быть и другие, где GET имеет смысл, но я не могу придумать ни одного в данный момент.
TTT
Проблема с побочным эффектом GET в равной степени относится и к подтверждению по электронной почте. Теперь клиент, перейдя по ссылке, подтвердит учетную запись, созданную кем-то другим с помощью вашей электронной почты, что позволит ему выдать себя за вас.
CodesInChaos
@CodesInChaos - это натяжка. Олицетворение, о котором вы говорите, будет происходить от того же имени пользователя или публичного личного имени, а не от одного и того же адреса электронной почты, и это может произойти независимо от того, какой адрес электронной почты они используют (обычно, в любом случае, только сервер знает адрес электронной почты владельца учетной записи). Кроме того, было бы бессмысленно создавать учетную запись с чужим адресом электронной почты. Как это может помочь им? Они не могли контролировать свою учетную запись.
TTT