Autorytatywna pozycja zduplikowanych kluczy zapytań HTTP GET

137

Mam problem ze znalezieniem autorytatywnych informacji o zachowaniu w zduplikowanych polach ciągu zapytania HTTP GET, takich jak

http://example.com/page?field=foo&field=bar 

aw szczególności czy zamówienie jest zachowane czy nie. Większość języków zorientowanych na sieć tworzy tablicę zawierającą zarówno foo, jak i bar skojarzone z kluczowym „polem”, ale chciałbym wiedzieć, czy istnieje autorytatywne stwierdzenie (np. W specyfikacji RFC) dotyczące tego punktu. RFC 3986 zawiera sekcję 3.4. Query, która odwołuje się do par klucz = wartość, ale nie powiedziano nic o tym, jak interpretować kolejność, zduplikowane pola i tak dalej. Ma to sens, ponieważ jest zależne od zaplecza, a nie w zakresie tego RFC ...

Chociaż faktycznie istnieje standard, chciałbym zobaczyć wiarygodne jego źródło, tak z ciekawości.

Stefano Borini
źródło
Też się nad tym zastanawiałem. Inną rzeczą jest specyfikacja dotycząca łączenia parametrów z ciągu zapytania z parametrami w treści POST.
Thilo
Na ranczu kodów ludzie mówią, że nie ma gwarancji zamówienia. Ale ten wątek jest stary i nikt go w żaden sposób nie popiera: coderanch.com/t/357197/Servlets/java/getParameterValues-order
Thilo
1
Oprócz tego, że serwer utrzymuje kolejność ciągu zapytania, pojawia się również pytanie o to, czy przeglądarka wysyła je w kolejności DOM (lub innej ustalonej).
Thilo

Odpowiedzi:

112

Nie ma specyfikacji na ten temat. Możesz robić, co chcesz.

Typowe podejścia obejmują: pierwszy-dany, ostatni-dany, tablica-wszystkiego, łańcuch-łączony-z-przecinkiem-wszystkiego.

Załóżmy, że surowe żądanie to:

GET /blog/posts?tag=ruby&tag=rails HTTP/1.1
Host: example.com

Istnieją różne opcje tego, co request.query['tag']powinno dać, w zależności od języka lub struktury:

request.query['tag'] => 'ruby'
request.query['tag'] => 'rails'
request.query['tag'] => ['ruby', 'rails']
request.query['tag'] => 'ruby,rails'
yfeldblum
źródło
12
Co więcej, istnieje również opcja ['rails', 'ruby'] (inna kolejność).
Thilo,
2
Z pewnością można zrobić wiele rzeczy.
yfeldblum
7
.NET da ci jako tablicę (nie przejmowałem się kolejnością, kiedy to testowałem), PHP da ci zawsze ostatnią, a Java (przynajmniej system, z którym pracowałem oparty na Javie) zawsze pierwszą wartość. stackoverflow.com/questions/1809494/…
SimonSimCity
17
Jest to oparte na ataku o nazwie HTTP Parameter Pollution i zostało przeanalizowane przez OWASP: owasp.org/images/b/ba/AppsecEU09_CarettoniDiPaola_v0.8.pdf Na stronie 9 znajduje się lista 20 systemów i opis ich obsługi ten przypadek.
SimonSimCity,
1
@SimonSimCity Oprócz tego PHP faktycznie utworzy tablicę, jeśli dodasz nawiasy kwadratowe z opcjonalnym indeksem do nazwy parametru.
Martin Ender,
14

Mogę potwierdzić, że dla PHP (przynajmniej w wersji 4.4.4 i nowszych) działa to tak:

GET /blog/posts?tag=ruby&tag=rails HTTP/1.1
Host: example.com

prowadzi do:

request.query['tag'] => 'rails'

Ale

GET /blog/posts?tag[]=ruby&tag[]=rails HTTP/1.1
Host: example.com

prowadzi do:

request.query['tag'] => ['ruby', 'rails']

To zachowanie jest takie samo dla danych GET i POST.

SimonSimCity
źródło
1
[]Przyrostek wydaje się bardzo dziwne zachowanie, ale jeśli spróbujesz wysłać tablicę jako argument za pośrednictwem jQuery .ajax(), a następnie zostanie ona automatycznie dodać je do Ciebie w ten sam sposób. Wygląda na to, że jest to korzystne dla użytkowników PHP.
Ian Clark
4
@IanClark Jest intuicyjny dla programistów PHP - w zwykłym PHP jest $foo[] = 1dołączany do tablicy. Django (Python) również robi to samo.
Izkata
Może zweryfikować na Apache Tomcat, zwraca ciągi połączone przecinkami.
Gaurav Ojha
8

Odpowiedź yfeldblum jest doskonała.

Tylko uwaga o piątym zachowaniu, które zauważyłem niedawno: w Windows Phone otwarcie aplikacji z uri ze zduplikowanym kluczem zapytania spowoduje NavigationFailed with:

System.ArgumentException: element z tym samym kluczem został już dodany.

Winowajcą jest System.Windows.Navigation.UriParsingHelper.InternalUriParseQueryStringToDictionary(Uri uri, Boolean decodeResults).

Więc system nawet nie pozwoli ci obsłużyć tego tak, jak chcesz, zabroni tego. Pozostaje Ci jedyne rozwiązanie pozwalające wybrać własny format (CSV, JSON, XML, ...) i uri-escape-it.

Cœur
źródło
2
Wygląda to raczej na wewnętrzny błąd tej funkcji niż na wybór projektu. Prawdopodobnie funkcja nie sprawdza duplikatów kluczy w tworzonym słowniku. Słowniki wymagają oczywiście unikalnych kluczy.
gligoran
1
Czyli przeglądarka klienta - a nie serwer - zgłasza błąd w tej sytuacji? Wygląda na to, że to błąd. Zastanawiam się, czy ten błąd nadal istnieje dzisiaj?
Jon Schneider,
1
@JonSchneider Tak, klient zgłasza NavigationFailedtaki identyfikator URI. Ale, wybaczcie, porzuciłem programowanie dla Windows (Phone) miesiąc po tym poście i przeniosłem się na macOS (iOS), więc nie mogę już pomóc w śledzeniu tego problemu w dzisiejszych czasach.
Cœur
5

Większość (wszystkich?) Frameworków nie daje żadnych gwarancji, więc załóżmy, że zostaną zwrócone w losowej kolejności.

Zawsze wybieraj najbezpieczniejsze podejście.

Na przykład interfejs Java HttpServlet: ServletRequest.html # getParameterValues

Nawet metoda getParameterMap nie wspomina o kolejności parametrów (nie można również polegać na kolejności iteratora java.util.Map).

Photodeus
źródło
3

Zwykle zduplikowane wartości parametrów, takie jak

http://example.com/page?field=foo&field=bar

wynik w pojedynczym parametrze queryString, który jest tablicą:

field[0]=='foo'
field[1]=='bar'

Widziałem to zachowanie w ASP, ASP.NET i PHP4.

3Dave
źródło
dokładnie, to jest de facto standard, ale o ile widzę, nie ma w tej sprawie autorytatywnej decyzji. Ponieważ nie wierzę, że tak jest, jestem po prostu nieudolny, aby to znaleźć.
Stefano Borini
2
Tak, prawdopodobnie każdy widział takie zachowanie. Pytanie brzmiało, czy rzeczywiście gdzieś to zostało określone.
Thilo
-1

Miałem to samo pytanie. Piszę funkcję javascript do analizowania i stringify zapytań. Nie wiem, czy ciąg zapytania ma zduplikowane nazwy lub nazwę w nawiasach, na przykład x [] = 1 i x [] = 2, jest standardem, chociaż niektóre języki obsługują ten format.

Ale uważam, że Chrome i Firefox mają nową klasę o nazwie URLSeachParamsi obsługują tylko najprostszy format, jak name=value. Jeśli w ciągu zapytania znajdują się zduplikowane nazwy, getmetoda URLSearchParamszwraca tylko pierwszą.

Więc osobiście, być może najprostszy adres URL bez zduplikowanych nazw jest znacznie bezpieczniejszy na przyszłość.

LCB
źródło
1
Jeśli w ciągu zapytania znajdują się zduplikowane nazwy, metoda get z URLSearchParams zwraca tylko pierwszą. To nie jest poprawne: możesz pobrać całą wartość jako tablicę za pomocąURLSearchParams.getAll('x')
Blaise
@Blaise Dziękuję bardzo, źle zrozumiałem tę funkcję wcześniej.
LCB