Dlaczego żądanie GET nie powinno zmieniać danych na serwerze?

109

W całym Internecie widzę następujące porady:

GET nigdy nie powinien zmieniać danych na serwerze - użyj do tego żądania POST

Jaka jest podstawa tego pomysłu?

Jeśli utworzę usługę php, która wstawi dane do bazy danych i przekażę jej parametry w ciągu zapytania GET, dlaczego tak się dzieje? (Używam przygotowanych instrukcji, aby zająć się SQL Injection). Czy żądanie POST jest w jakiś sposób bardziej bezpieczne?

Czy może ma to jakiś historyczny powód? Jeśli tak, to jak ważna jest dzisiaj ta rada?

Devdatta Tengshe
źródło
Dziękuję, że zadałeś to pytanie, i dziękuję @Odpowiedzi za dobrze sformułowaną odpowiedź Zawsze potrzebowałem referencji, aby wysłać osoby, które zadają to pytanie w kierunku :)
Benjamin Gruenbaum
Zobacz także HTTP PUT - stackoverflow.com/questions/630453/put-vs-post-in-rest (z uwagami na temat bycia idempotentem)
Bratch
2
@ JoachimSauer Podczas gdy GET zapisałby je przed przeszukiwaczem, głównym problemem był brak uwierzytelnienia. Każdy skrypciarz mógł również POST ich zapomnieć.
CodesInChaos

Odpowiedzi:

185

To nie jest rada.

A GETjest zdefiniowane w ten sposób w protokole HTTP . Ma być idempotentny i bezpieczny .

Jeśli chodzi o to, dlaczego - a GETmożna buforować, a w przeglądarce odświeżono. Raz po raz po raz.

Oznacza to, że jeśli zrobisz to samo GETjeszcze raz, będzie wstawić do bazy danych ponownie .

Zastanów się, co to może znaczyć, jeśli GETstaje się linkiem i jest indeksowane przez wyszukiwarkę. Baza danych będzie pełna zduplikowanych danych.

Sugeruję również czytanie identyfikatorów URI, adresowalności oraz stosowanie HTTP GET i POST .


Występuje również problem z pobieraniem linków w niektórych przeglądarkach - będą one wywoływać pobieranie linków, nawet jeśli nie zostało to zaznaczone przez autora strony.

Jeśli, powiedzmy, twoje wylogowanie jest za „GET”, linkowanym z każdej strony w Twojej witrynie, ludzie mogą się wylogować właśnie z powodu tego zachowania.

Oded
źródło
35
Wiele, wiele, wiele narzędzi, narzędzi, przeszukiwaczy sieci i inne rzeczy zakładają, że GETnigdy nie będzie to destrukcyjne działanie (słusznie, skoro jest to określone w ten sposób). Jeśli teraz zepsujesz aplikację, psując jej specyfikację, zachowasz obie części aplikacji.
Joachim Sauer
7
@NimChimpsky: zmienia się przez GET. Ta rada jest po prostu błędna. Bezpieczny oznacza, że ​​użytkownik nie może być pociągnięty do odpowiedzialności za skutki uboczne, nie że nie może być żadnych skutków ubocznych. W przeciwnym razie nie możesz mieć plików dziennika dla swojego serwera, co byłoby absurdalne! Jest to dość jasno określone w sekcji 9.1.1 RFC2616.
Jörg W Mittag
8
@ JörgWMittag: Nie powiedziałbym „po prostu źle”, powiedziałbym „sformułowanie nieskutecznie”. GET nie powinien mieć zmiany, ponieważ jest celem. Oczywiście możesz liczyć, rejestrować i obserwować żądanie GET. Nie powinno to jednak modyfikować rzeczywistych danych biznesowych.
Joachim Sauer
23
@NimChimpsky A GETnie powinien zmieniać zasobu wymaganego przez GET, ale to nie znaczy, że „nic na serwerze nie powinno się zmienić”. Oczywiście rzeczy takie jak dzienniki, liczniki i inny stan serwera mogą ulec zmianie podczas każdego żądania.
Eric King
8
Kilka lat temu Google wydało dodatek do przeglądarki (iirc), który wstępnie pobierałby strony za pomocą linków. Stało się tak również w przypadku niektórych źle zaprojektowanych paneli kontrolnych - adresy URL spowodowałyby zapis lub coś, co mogłoby zostać zapisane lub nawet usunięte na serwerze (think post? Action = delete). Spowodowało to, że akcje zostały wykonane bez wiedzy użytkownika. Google przerwał ten dodatek z tego powodu, iirc, nawet jeśli to wina producenta aplikacji internetowej polegająca na użyciu GET do zmiany stanu.
Cthulhu
24

Każdy czasownik HTTP ma swoją własną odpowiedzialność. Na przykład GETzgodnie z definicją RFC

oznacza pobranie dowolnej informacji (w formie encji) zidentyfikowanej przez URI żądania.

POSTz drugiej strony oznacza wstawkę lub bardziej formalnie

Metoda POST służy do żądania, aby serwer źródłowy zaakceptował
jednostkę zawartą w żądaniu jako nowego podwładnego zasobu
określonego przez identyfikator URI żądania w wierszu żądania

Powody, dla których warto to zachować:

  • Jest to bardzo proste i działa w skali globalnej Internetu od 1991 roku
  • Trzymaj się zasady pojedynczej odpowiedzialności
  • Inne strony używają, GETaby działać jako środek wyszukiwania informacji i eksploracji danych
  • Zakłada się, że GET jest bezpieczną operacją, która nigdy nie zmienia stanu zasobu
  • Względy bezpieczeństwa GETsą w istocie odczytem , podczas gdy w POSTrzeczywistości stanowią zapis
  • GET jest buforowany przez przeglądarki, węzły w sieci, dostawców usług internetowych
  • O ile treść nie ulegnie zmianie, GETten sam adres URL musi zwrócić te same wyniki wszystkim użytkownikom, w przeciwnym razie nie będziesz mieć zaufania do zwracanego wyniku

Dla kompletności i tylko w celu wymuszenia prawidłowego użycia (źródło) :

  • GETparametry są przekazywane jako część adresu URL, który domyślnie ma małą i ograniczoną długość 256 znaków, a niektóre serwery obsługują ponad 4000 znaków. Jeśli chcesz wstawić długi rekord, nie ma uzasadnionego sposobu na przekazanie tych danych
  • Podczas korzystania z bezpiecznego połączenia, ̶ takim jak TLS, ̶ URL jest nie dostaję szyfrowane, ̶ stąd wszystkie parametry ̶ ̶G̶E̶T̶̶ są przesyłane jako zwykły tekst. Adres URL jest zaszyfrowany za pomocą TLS, więc TLS jest w porządku.
  • Wstawianie danych binarnych lub znaków spoza ASCII GETjest niepraktyczne
  • GET jest ponownie wykonywane, jeśli użytkownik naciśnie przycisk Wstecz w przeglądarce
  • Niektóre starsze roboty mogą nie indeksować adresów URL ze ?znakiem wewnątrz
oleksii
źródło
1
Czy jesteś pewien, że adres URL nie jest zaszyfrowany przez TLS? Miałem wrażenie, że uzgadnianie SSL / TLS następuje przed przesłaniem nagłówków HTTP. Z tego powodu wirtualny hosting witryn HTTPS na jednym adresie IP jest trudny. Czy się mylę?
Brandon
Zgadza się, naprawiłem to
oleksii
2
@Brandon Nowoczesne przeglądarki wysyłają domenę hosta w sposób jawny w ramach uzgadniania TLS (znanego jako wskazanie nazwy serwera), aby umożliwić hosting więcej niż jednej domeny na adres IP. Część adresu URL ścieżki / zapytania jest chroniona przez TLS. Pod tym względem nie ma różnicy między GET i innymi czasownikami HTTP.
CodesInChaos
9

EDYCJA: Wcześniej powiedziałem, że POST pomaga chronić cię przed CSRF, ale to źle. Nie przemyślałem tego poprawnie. Musisz wymagać unikalnego ukrytego tokena o zasięgu sesji we wszystkich swoich żądaniach zmiany danych w celu ochrony przed CSRF.

Na początku Internetu istniały akceleratory przeglądarki. Programy te zaczną klikać łącza na stronie w celu buforowania zawartości. Google Web Accelerator był jednym z tych programów. Może to spowodować spustoszenie w aplikacji, która wprowadza zmiany po kliknięciu łącza. Zakładam, że nadal są ludzie używający oprogramowania przyspieszającego.

Serwery proxy i przeglądarki będą buforować żądania GET, więc gdy użytkownik ponownie wejdzie na stronę, może nie wysłać żądania do aplikacji, więc użytkownik myśli, że podjął działanie, ale tak naprawdę nie zrobił.

Sarel Botha
źródło
1
CSRF jest równie możliwe z GET i POST. Na przykład atakujący może zamieścić na swojej stronie formularz automatycznego przesyłania w celu uruchomienia żądania POST. Standardowe podejście do zapobiegania CSRF zawiera jawnie wartość nieznaną atakującemu w żądaniu (inaczej niż domyślnie obejmuje nagłówki plików cookie).
CodesInChaos
8

Jeśli utworzę usługę php, która wstawi dane do bazy danych i przekażę jej parametry w ciągu zapytania GET, dlaczego tak się dzieje?

Najprostsza odpowiedź brzmi: „bo to nie to GETznaczy”.

Przekazywanie GETdanych do aktualizacji jest jak pisanie listu miłosnego i wysyłanie go w kopercie z napisem „OFERTA SPECJALNA - DZIAŁAJ TERAZ!” W obu przypadkach nie powinieneś być zaskoczony, że odbiorca i / lub pośrednicy źle obchodzą się z Twoją wiadomością .

Nathan Long
źródło
5

Do operacji CRUD w aplikacji zorientowanej na bazę danych użyj następującego schematu:

Użyj HTTP GET do operacji odczytu (SQL SELECT)

Użyj HTTP PUT do operacji aktualizacji (SQL UPDATE)

Użyj HTTP POST do tworzenia operacji (SQL INSERT)

Użyj HTTP DELETE dla operacji usuwania (SQL DELETE)


źródło
3
Put vs post nie jest taki, jak twierdzisz. Put ma miejsce, gdy klient modyfikuje zasób w dokładnie określonej lokalizacji. W przypadku postu serwer ostatecznie decyduje o dokładnym Uri dla zasobu.
Andy,
Czy HTTP PUT nie przypomina raczej SQL DELETE i INSERT niż UPDATE? Również SQL UPDATE może aktualizować wiele rekordów jednocześnie, ale HTTP PUT zaktualizuje tylko jedną rzecz.
Backwards_Dave
0

GET nigdy nie powinien zmieniać danych na serwerze - użyj do tego żądania POST

Ta rada i wszystkie odpowiedzi tutaj są błędne. Oczywiście jestem zbyt dramatyczny, inne odpowiedzi są doskonałe, ale uważam, że dokładna rada powinna być udzielona w następujący sposób:

GET rzadko powinien zmieniać dane na serwerze - użyj do tego żądania POST

Stwierdzenie, że „nigdy” nie jest zbyt ekstremalne, i chociaż inne odpowiedzi tutaj dokładnie wyjaśniają, dlaczego powinieneś „rzadko” to robić, istnieje kilka scenariuszy, w których zmiana danych za pomocą GET jest całkowicie uzasadniona. Przykładem jest jednorazowy link weryfikacyjny e-mail. Zazwyczaj łącza te zawierają identyfikator GUID, który po uzyskaniu dostępu będzie musiał zmienić dane. Jeśli poprawnie zaimplementowane kolejne identyczne żądania GET zostaną zignorowane.

Jest to oczywiście przypadek skrajny, ale z pewnością warty odnotowania.

TTT
źródło
3
Co się stanie, jeśli klient pocztowy zdecyduje się pobrać link bez klikania go? Na przykład dlatego, że chce go przeskanować w poszukiwaniu złośliwego oprogramowania. Właściwe podejście do anulowania subskrypcji prowadzi do strony, na której użytkownik może kliknąć przycisk, aby anulować subskrypcję (gdzie kliknięcie przycisku uruchamia żądanie POST).
CodesInChaos
@CodesInChaos - doskonały punkt! Zgadzam się z Tobą. Usunąłem przykład rezygnacji z subskrypcji i pozostawiłem weryfikację adresu e-mail jako jedyny przykład. Mogą istnieć inne niż weryfikacja adresu e-mail, gdzie GET ma sens, ale w tej chwili nie mogę o tym myśleć.
TTT
Problem z działaniem niepożądanym GET dotyczy w równym stopniu potwierdzenia e-mailem. Teraz klient podążający za linkiem potwierdzi konto, które ktoś utworzył za pomocą Twojego adresu e-mail, umożliwiając mu podszywanie się pod Ciebie.
CodesInChaos
@CodesInChaos - to jest odcinek. Podszywanie się, o którym mówisz, pochodzi od tej samej nazwy użytkownika lub publicznej nazwy osobistej, a nie od tego samego adresu e-mail, i może się to zdarzyć niezależnie od tego, którego adresu e-mail używa (zazwyczaj tylko serwer zna adres e-mail właściciela konta). Poza tym założenie konta z czyimś adresem e-mail byłoby bezcelowe. Jak to może im pomóc? Nie mogli kontrolować własnego konta.
TTT,