Чому GET-запит не повинен змінювати дані на сервері?


109

У всьому Інтернеті я бачу таку пораду:

GET ніколи не повинен змінювати дані на сервері - використовуйте для цього POST-запит

Що є основою цієї ідеї?

Якщо я роблю сервіс php, який вставляє дані в базу даних і передає їх параметри в рядок запиту GET, чому це неправильно? (Я використовую підготовлені заяви, щоб подбати про ін'єкцію SQL). Чи запит POST якимось чином більш безпечний?

Або для цього є якась історична причина? Якщо так, наскільки ця порада сьогодні діє?




Дякую за те, що ви задали це запитання, і дякую вам @Oded за добре сформульовану відповідь, мені завжди потрібна була посилання, щоб надсилати людей, які задають це питання на адресу :)
Benjamin Gruenbaum

Також дивіться HTTP PUT - stackoverflow.com/questions/630453/put-vs-post-in-rest (із зауваженнями про те, що є
ідентичним

2
@JoachimSauer Хоча GET врятував би їх від сканера, основною проблемою була відсутність автентифікації. Будь-який скрипт kiddy міг би і POSTed їх забути.
CodesInChaos

Відповіді:


185

Це не поради.

A GETвизначено таким чином у протоколі HTTP . Він повинен бути безсильним і безпечним .

Щодо того, чому - а GETможе бути кешований і в браузері, оновлений. Знову і знову і знову.

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

Поміркуйте, що це може означати, якщо GETперехід стає посиланням, і пошукова система його сканує. У вас буде база даних повна дублікатів даних.

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


Існує також проблема з попереднім завантаженням посилань у деяких браузерах - вони будуть закликати попередньо отримувати посилання, навіть якщо автор цього не вказав.

Якщо, скажімо, ваш вихід із-за "GET", пов’язаний із кожною сторінкою вашого веб-сайту, люди можуть вийти із системи саме завдяки цій поведінці.


35
Багато, багато, багато інструментів, утиліт, веб-сканерів та інших предметівмаджиггі вважають, що GETніколи не буде руйнівної дії (справедливо, оскільки це визначено саме так). Якщо зараз ви порушите свою програму, порушивши цю специфікацію, ви отримаєте можливість зберегти обидві частини програми.
Йоахім Зауер

7
@NimChimpsky: це дійсно змінюється а GET. Ця порада просто неправильна. Безпечний означає, що користувач не може нести відповідальність за побічні ефекти, а також не може бути побічних ефектів. Інакше у вас не могли бути файли журналів для вашого сервера, що було б абсурдно! Це досить чітко прописано в розділі 9.1.1 RFC2616.
Йорг W Міттаг

8
@ JörgWMittag: Я б не сказав "просто неправильно", я б сказав "фразово недосконало". У GET не повинно бути змін, оскільки це є метою. Звичайно, вам дозволяється рахувати, реєструвати та дотримуватися GET-запиту. Але він не повинен змінювати фактичні дані вашої компанії.
Йоахім Зауер

23
@NimChimpsky A GETне повинен змінювати ресурс, про який вимагає GET, але це не означає, що "нічого на сервері не повинно змінюватися". Звичайно, такі речі, як журнали, лічильники та інший стан сервера, можуть змінюватися під час будь-якого запиту.
Ерік Кінг

8
Досить кілька років тому Google випустив надбудову для браузера (iirc), яка попередньо вибирала сторінки за посиланнями. Це траплялося і на деяких панелях управління, які були розроблені погано - URL-адреси призведе до того, що запис або щось буде записане або навіть видалене на сервері (think post? Action = delete). Це спричинило виконання дій, не знаючи цього користувача. Google відмінив цей аддон з цієї причини, iirc, навіть якщо виною виробника webapp було використання GETs для зміни стану.
Cthulhu

24

Кожне дієслово HTTP несе свою відповідальність. Наприклад GET, як визначено RFC

означає отримати будь-яку інформацію (у формі сутності), ідентифіковану URI-запитом.

POST, з іншого боку, означає вставити або більше формально

Метод POST використовується для запиту, щоб сервер-джерело приймав
сутність, включену в запит, як новий підлеглий ресурс,
ідентифікований 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

EDIT: Раніше я говорив, що 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 оновить лише одне.
назад_Dave

0

GET ніколи не повинен змінювати дані на сервері - використовуйте для цього POST-запит

Ця порада, і всі відповіді тут неправильні. Очевидно, що я надто драматичний, інші відповіді - чудові, але я вважаю, що точну пораду потрібно дати:

GET рідко повинен змінювати дані на сервері - використовуйте для цього POST-запит

Сказати "ніколи" не надто екстремально, і хоча інші відповіді тут точно пояснюють, чому ви повинні "рідко" робити це, є деякі сценарії, коли цілком розумно змінювати дані за допомогою GET. Приклад - посилання для підтвердження одноразового використання електронної пошти. Зазвичай ці посилання містять GUID, що при зверненні доведеться змінювати дані. Якщо правильно реалізовані наступні однакові GET-запити, вони будуть ігноровані.

Це, очевидно, кращий випадок, але, безумовно, варто зазначити.


3
Що робити, якщо ваш mailclient вирішить отримати посилання, не натискаючи на нього? Наприклад, тому що він хоче сканувати його на наявність зловмисного програмного забезпечення. Правильний підхід для скасування підписки - це призвести до сторінки, де користувач може натиснути кнопку, щоб скасувати підписку (де натискання кнопки викликає запит POST).
CodesInChaos

@CodesInChaos - чудовий момент! Я погоджуюсь з тобою. Я видалив приклад скасування підписки і залишив підтвердження електронної пошти як єдиний приклад. Окрім підтвердження електронною поштою, можуть бути інші, де GET має сенс, але наразі я не можу думати про це.
TTT

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

@CodesInChaos - це розтяжка. Уособлення, про яке ви говорите, походитиме від того самого імені користувача або загальнодоступного особистого імені, а не тієї самої адреси електронної пошти, і це може статися незалежно від того, яку адресу електронної пошти вони використовують (як правило, лише той сервер знає електронну адресу власника облікового запису). Крім того, було б безглуздо створювати обліковий запис з чужою електронною адресою. Як це могло їм допомогти? Вони не могли контролювати власний рахунок.
TTT
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.