Które znaki powodują, że adres URL jest nieprawidłowy?

514

Które znaki powodują, że adres URL jest nieprawidłowy?

Czy to są prawidłowe adresy URL?

  • example.com/file[/].html
  • http://example.com/file[/].html
dobrze
źródło
42
Podczas sprawdzania poprawności zawsze powinieneś „myśleć pozytywnie”: poprosić o „co jest ważne”, wszystko inne jest nieprawidłowe. Testowanie z (kilkoma) prawidłowymi znakami jest znacznie bezpieczniejsze (i łatwiejsze!) Niż wszystkie możliwe nieprawidłowe znaki.
mfx,

Odpowiedzi:

600

Zasadniczo identyfikatory URI zdefiniowane w RFC 3986 (patrz sekcja 2: Znaki ) mogą zawierać dowolny z następujących 84 znaków:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=

Zauważ, że ta lista nie określa, gdzie w URI mogą wystąpić te znaki.

Każdy inny znak musi być zakodowany za pomocą metody procentowej ( %hh). Każda część identyfikatora URI ma dalsze ograniczenia dotyczące tego, jakie znaki muszą być reprezentowane przez słowo zakodowane w procentach.

Gumbo
źródło
31
(oczywiście lista znaków nie określa, w którym miejscu mogą się pojawić)
Eamon Nerbonne
75
Oto regex, który określi, czy cały ciąg zawiera tylko powyższe znaki: / ^ [! # $ & -; =? - [] _ ​​a-z ~] + $ /
Leif Wickland
43
@techiferous, Tak, zapomniałem dopuścić znaków „%”. Powinno to wyglądać bardziej: /^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ Czy było coś, co odkryłeś, że powinno to akceptować? (Żeby było jasne, to wyrażenie regularne sprawdza tylko, czy ciąg zawiera prawidłowe znaki adresu URL, a nie czy ciąg zawiera dobrze sformułowany adres URL.)
Leif Wickland,
12
@Timwi RFC 3986 mówi: „Oktet kodowany procentowo jest kodowany jako tryplet znakowy, składający się z znaku procentowego„% ”, po którym następują dwie cyfry szesnastkowe reprezentujące wartość liczbową tego oktetu”. Mówi także: „Ponieważ znak procentu („% ”) służy jako wskaźnik dla oktetów zakodowanych procentowo, musi być zakodowany procentowo jako„% 25 ”, aby ten oktet mógł zostać użyty jako dane w URI.” Przeczytałem to, mówiąc, że „%” może pojawić się tylko wtedy, gdy po nim następują dwie cyfry szesnastkowe. Jak to czytasz?
Leif Wickland,
13
@Weeble Moje wyrażenie regularne zawierało te znaki przy użyciu zakresów. Pomiędzy i ';' i pomiędzy „?” i „[” znajdziesz wszystkie postacie, których nie widziałeś.
Leif Wickland
193

Aby dodać wyjaśnienia i bezpośrednio odpowiedzieć na powyższe pytanie, istnieje kilka klas znaków, które powodują problemy z adresami URL i identyfikatorami URI.

Niektóre znaki są niedozwolone i nigdy nie powinny pojawiać się w adresie URL / URI, znakach zastrzeżonych (opisanych poniżej) i innych znakach, które mogą powodować problemy w niektórych przypadkach, ale są oznaczone jako „nierozsądne” lub „niebezpieczne”. Wyjaśnienia, dlaczego znaki są ograniczone, są jasno określone w RFC-1738 (adresy URL) i RFC-2396 (URI). Uwaga: nowsza wersja RFC-3986 (aktualizacja RFC-1738) definiuje konstrukcję dozwolonych znaków w danym kontekście, ale starsza specyfikacja oferuje prostszy i bardziej ogólny opis, które znaki są niedozwolone przy zastosowaniu następujących reguł.

Wykluczone znaki US-ASCII niedozwolone w składni URI:

   control     = <US-ASCII coded characters 00-1F and 7F hexadecimal>
   space       = <US-ASCII coded character 20 hexadecimal>
   delims      = "<" | ">" | "#" | "%" | <">

Znak „#” jest wykluczony, ponieważ służy do oddzielenia identyfikatora URI od identyfikatora fragmentu. Znak procentu „%” jest wykluczony, ponieważ jest używany do kodowania znaków specjalnych. Innymi słowy, „#” i „%” są znakami zastrzeżonymi, których należy użyć w określonym kontekście.

Lista niemądrych znaków jest dozwolona, ​​ale może powodować problemy:

   unwise      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"

Znaki zastrzeżone w komponencie zapytania i / lub mające specjalne znaczenie w URI / URL:

  reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","

„Zarezerwowana” klasa składni powyżej odnosi się do tych znaków, które są dozwolone w ramach identyfikatora URI, ale które mogą nie być dozwolone w ramach określonego komponentu ogólnej składni URI. Znaki w zestawie „zastrzeżone” nie są zarezerwowane we wszystkich kontekstach . Na przykład nazwa hosta może zawierać opcjonalną nazwę użytkownika, więc może to być coś w rodzaju, ftp://user@hostname/gdzie znak „@” ma specjalne znaczenie.

Oto przykład adresu URL, który zawiera niepoprawne i nierozsądne znaki (np. „$”, „[”, „]”) I powinien być odpowiednio zakodowany:

http://mw1.google.com/mw-earth-vectordb/kml-samples/gp/seattle/gigapxl/$[level]/r$[y]_c$[x].jpg

Niektóre ograniczenia znaków dla identyfikatorów URI / adresów URL zależą od języka programowania. Na przykład „|” (0x7C), chociaż tylko oznaczony jako „nierozsądny” w specyfikacji URI, wyrzuci wyjątek URISyntaxException do konstruktora Java java.net.URI, więc adres URL podobny http://api.google.com/q?exp=a|bjest niedozwolony i zamiast tego należy go zakodować, tak jak http://api.google.com/q?exp=a%7Cbprzy użyciu Java z instancją obiektu URI.

JasonM1
źródło
2
Doskonała, dokładna odpowiedź, jedyna, która bezpośrednio odpowiada na aktualne pytanie. Sekcja zarezerwowana może wymagać pracy, np. Dosłowność ?jest w porządku w sekcji zapytania, ale przedtem jest niemożliwa i nie sądzę, że @należy do żadnej z tych list. Aha, a nie %25w ostatnim ciągu, nie masz na myśli %7C?
Bob Stein
1
Dzięki. Dobry haczyk:% 25 w tym przykładzie było literówką. Dodano przypis do „zarezerwowanego” opisu składni bezpośrednio z RFC-2396.
JasonM1,
1
Ta odpowiedź nie jest zła , ale są pewne zamieszania i błędy. Początkowo łączysz niedozwolone i zastrzeżone znaki (bardzo różne rzeczy), zbytnio rozróżniasz znaki „nierozsądne” i inne niedozwolone znaki (upuszczone w RFC 3986 i składniowo nieistotne nawet w RFC 2396), i mylnie przedstawiasz listę wszystkie zastrzeżone znaki jako lista zarezerwowana „w ramach komponentu zapytania” .
Mark Amery
1
Dzięki, nie chciałem grupować niedozwolonych i zarezerwowanych tak samo. Zaktualizowałem odpowiedź. Reguły IMHO w RFC-2396, choć starsze, są łatwiejsze do zrozumienia niż zaktualizowane reguły w 3986. Odpowiedź odzwierciedla bardziej, na których znakach mogą być kłopotliwe, niż dokładnie to, w jakim kontekście jest to dozwolone lub niedozwolone.
JasonM1 17.04.16
1
Warto zauważyć, że Tomcat w ostatnich wydaniach (7.0.73+, 8.0.39+, 8.5.7+) zaczął odrzucać żądania ze znakami z kategorii „nierozsądne” z błędami HTTP 400: „W celu żądania znaleziono nieprawidłowy znak. prawidłowe znaki są zdefiniowane w RFC 7230 i RFC 3986 ”
Philip
100

Większość istniejących tutaj odpowiedzi jest niepraktyczna, ponieważ całkowicie ignorują rzeczywiste użycie adresów, takich jak:

Najpierw dygresja w terminologii. Jakie te adresy? Czy są to prawidłowe adresy URL?

Historycznie odpowiedź brzmiała „nie”. Zgodnie z RFC 3986 od 2005 r. Takie adresy nie są identyfikatorami URI (a zatem nie są adresami URL, ponieważ adresy URL są rodzajem identyfikatorów URI ). Zgodnie z terminologią standardów IETF z 2005 r. Powinniśmy właściwie nazywać je IRI (Internacjonalizowane identyfikatory zasobów), jak zdefiniowano w RFC 3987 , które technicznie nie są identyfikatorami URI, ale mogą być konwertowane na identyfikatory URI poprzez proste kodowanie procentowe wszystkich znaków spoza ASCII w IRI .

Według współczesnej specyfikacji odpowiedź brzmi „tak”. WHATWG standardu życia po prostu klasyfikuje wszystko, czego wcześniej się nazywać „URI” lub „IRIS” AS „URL”. To wyrównuje specced terminologia z jak normalni ludzie, którzy nie czytali spec używać słowa „URL”, który był jednym z Spec za celami .

Jakie postacie są dozwolone w ramach WHATWG Living Standard?

Jakie znaki są dozwolone w nowym znaczeniu „URL”? W wielu częściach URL, takich jak ciąg kwerendy i ścieżki, mamy możliwość korzystania arbitralnych „jednostek URL” , które są

Punkty kodu URL i bajty zakodowane procentowo .

Co to są „punkty kodu URL”?

Te punkty kodowe adresu URL to ASCII alfanumeryczny U + 0021 (!), U + 0024 ($), U + 0026 (&), U + 0027 ( '), U + 0028 nawias U + 0029 nawiasie U + 002A (*), U + 002B (+), U + 002C (,), U + 002D (-), U + 002E (.), U + 002F (/), U + 003A (:), U + 003B (;), U + 003D (=), U + 003F (?), U + 0040 (@), U + 005F (_), U + 007E (~) i punkty kodowe w zakresie od U + 00A0 do U + 10FFFD włącznie, z wyłączeniem surogatów i znaków innych niż znaki.

(Uwaga: lista „punktów kodu URL” nie obejmuje %, ale %są one dozwolone w „Jednostkach kodu URL”, jeśli są one częścią sekwencji kodującej procentowo).

Jedynym miejscem, w którym mogę dostrzec, gdzie specyfikacja pozwala na użycie dowolnego znaku spoza tego zestawu, jest host , w którym zawarte są adresy IPv6 [i ]znaki. Gdzie indziej w adresie URL dozwolone są jednostki URL lub niektóre bardziej restrykcyjne zestawy znaków.

Jakie postacie były dozwolone na podstawie starych RFC?

Ze względu na historię, a ponieważ nie została ona w pełni zbadana gdzie indziej w odpowiedziach tutaj, zbadajmy dozwoloną pod starszą parą specyfikacji.

Przede wszystkim mamy dwa typy znaków zastrzeżonych RFC 3986 :

  • :/?#[]@, które są częścią ogólnej składni identyfikatora URI zdefiniowanego w RFC 3986
  • !$&'()*+,;=, które nie są częścią ogólnej składni RFC, ale są zarezerwowane do użycia jako składniki składniowe poszczególnych schematów URI. Na przykład, średniki i przecinki są stosowane jako część składni URI danych i &i =są stosowane jako część wszechobecnego ?foo=bar&qux=bazformacie w ciągi zapytania (który nie jest określony w specyfikacji RFC 3986).

Dowolny z zastrzeżonych znaków powyżej może być legalnie używany w URI bez kodowania, albo w celu spełnienia ich celu składniowego, albo po prostu jako dosłowne znaki w danych w niektórych miejscach, w których takie użycie nie mogło być źle interpretowane jako znak spełniający swój cel syntaktyczny. (Na przykład, chociaż /ma składniowe znaczenie w adresie URL, możesz użyć go niezakodowanego w ciągu zapytania, ponieważ nie ma ono znaczenia w ciągu zapytania).

RFC 3986 określa również niektóre niezarezerwowane znaki, których zawsze można użyć do przedstawienia danych bez żadnego kodowania:

  • abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~

Wreszcie %sam znak jest dopuszczony do kodowania procentowego.

Że pozostawia tylko następujących znaków ASCII, które są zakazane pojawianiu się w adresie URL:

  • Znaki kontrolne (znaki 0-1F i 7F), w tym nowy wiersz, tabulator i powrót karetki.
  • "<>\^`{|}

Każda inna postać z ASCII może legalnie występować w adresie URL.

Następnie RFC 3987 rozszerza ten zestaw niezastrzeżonych znaków o następujące zakresy znaków Unicode:

  %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF
/ %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD
/ %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD
/ %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD
/ %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD
/ %xD0000-DFFFD / %xE1000-EFFFD

Te wybory bloków ze starej specyfikacji wydają się dziwne i arbitralne, biorąc pod uwagę najnowsze definicje bloków Unicode ; dzieje się tak prawdopodobnie dlatego, że bloki zostały dodane do dekady od czasu napisania RFC 3987.


Wreszcie, być może warto zauważyć, że sama wiedza, które znaki mogą legalnie pojawić się w adresie URL, nie wystarcza, aby rozpoznać, czy dany ciąg jest legalnym adresem URL, czy nie, ponieważ niektóre znaki są dozwolone tylko w określonych częściach adresu URL. Na przykład znaki zastrzeżone [i ]są legalne jako część hosta dosłownego IPv6 w adresie URL takim jak http: // [1080 :: 8: 800: 200C: 417A] / foo, ale nie są legalne w żadnym innym kontekście, więc Przykład OP http://example.com/file[/].htmljest nielegalny.

Mark Amery
źródło
3
plusone za wyczerpujące referencje (np. RFC)
Yan Foto
19

W dodatkowym pytaniu zapytałeś, czy www.example.com/file[/].htmljest prawidłowym adresem URL.

Ten adres URL jest nieprawidłowy, ponieważ adres URL jest typem identyfikatora URI, a prawidłowy identyfikator URI musi mieć podobny schemat http:(patrz RFC 3986 ).

Jeśli chciałeś zapytać, czy http://www.example.com/file[/].htmljest to prawidłowy adres URL, odpowiedź brzmi „nie”, ponieważ znaki nawiasu kwadratowego są tam niepoprawne.

Znaki nawiasu kwadratowego są zarezerwowane dla adresów URL w tym formacie: http://[2001:db8:85a3::8a2e:370:7334]/foo/bar(tzn. Literał IPv6 zamiast nazwy hosta)

Warto dokładnie przeczytać RFC 3986, jeśli chcesz w pełni zrozumieć problem.

Dominic Sayers
źródło
Po przeczytaniu RFC jestem bardziej skłonny zgodzić się z bardziej szczegółowym wyjaśnieniem @Stephen C.
skolima
Adresy URL nie są podzbiorem URI. [I ]nie są ważne przez URI prawie parserami widziałem. To mnie naprawdę wkręciło w prawdziwym świecie: stackoverflow.com/questions/11038967/...
Adam Gent
Adresy URL @AdamGent bardzo stanowią podzbiór identyfikatorów URI. Jedyną różnicą między nimi jest to, czy opisują lokalizację zasobu - co jest rozróżnieniem semantycznym, a nie składniowym. Jeśli analizowane przez Ciebie parsery, które oznaczyły się jako parsery „URI”, traktowały nawiasy kwadratowe inaczej niż te, które oznaczały się jako parsery „URL”, to jest to czysty zbieg okoliczności, nie spowodowany różnicą między adresami URL i URI.
Mark Amery
@ Mark Amery jest analogiczny do powiedzenia, że ​​C ++ jest nadzbiorem C. Jest to w przeważającej części, ale nie do końca prawda, ponieważ (URL i C) są znacznie starsze, muszą zawierać mniej rygorystyczne zachowanie. Problem polega na tym, że parsery adresów URL będą analizować rzeczy, które nie są poprawnymi identyfikatorami URI ... Mam na myśli większość z nich (szczerze mówiąc, mam już dość wskazywania tego w tak wielu językach). To nie przypadek, że jest to kompatybilność wsteczna. Czy możemy się zgodzić, że specyfikacja adresu URL jest co najmniej starsza?
Adam Gent
@MarkAmery To jest z Python, C #, Java i niektórych bibliotek C, które parsery będą traktować Unwisebardzo poważnie dla URI, a mimo to będą w porządku z bibliotekami URL. Oznacza to, że nie ma flagi do zignorowania Unwise. Będę musiał sprawdzić, co Rust lang (ponieważ jest budowany dla przeglądarki, jestem ciekawy, co robi) dla adresów URL. Jednak większość przeglądarek również z radością przekazuje „[”, „]”. Teoretycznie, tak jak powiedziałem w C / C ++, są sub / super, ale rzeczywistość nie jest tak prawdziwa. Jest wysoce zależny od interpretacji specyfikacji i semantyki super / podzbioru.
Adam Gent
12

Wszystkie prawidłowe znaki, które mogą być użyte w URI ( URL to typ URI ) są zdefiniowane w RFC 3986 .

Wszystkie pozostałe znaki mogą być użyte w adresie URL, pod warunkiem, że są one najpierw „zakodowane w adresie URL”. Obejmuje to zmianę nieprawidłowego znaku dla określonych „kodów” (zwykle w postaci symbolu procentu (%), po którym następuje liczba szesnastkowa).

Ten link, HTML Encoding Reference , zawiera listę kodowań nieprawidłowych znaków.

CraigTP
źródło
A w przypadku znaków Unicode w artykule w Wikipedii Kodowanie procentowe mówi: „Ogólna składnia URI nakazuje, aby nowe schematy URI, które zapewniają reprezentację danych znaków w URI, muszą w rzeczywistości reprezentować znaki z niezarezerwowanego zestawu bez tłumaczenia, i powinien przekonwertować wszystkie pozostałe znaki na bajty zgodnie z UTF-8, a następnie kodować procentowo te wartości . ”
DavidRR
9

Kilka zakresów znaków Unicode jest prawidłowych HTML5 , chociaż ich użycie może nadal nie być dobrym pomysłem.

Np. hrefDoktorzy mówią : http://www.w3.org/TR/html5/links.html#attr-hyperlink-href :

Atrybut href w elementach a i area musi mieć wartość, która jest prawidłowym adresem URL potencjalnie otoczonym spacjami.

Następnie definicja „prawidłowego adresu URL” wskazuje na http://url.spec.whatwg.org/ , co oznacza, że ​​jego celem jest:

Dostosuj RFC 3986 i RFC 3987 do współczesnych implementacji i przestarzałe w tym procesie.

Ten dokument definiuje punkty kodu URL jako:

ASCII alfanumeryczne, „!”, „$”, „&”, „” „,” („,”) ”,„ * ”,„ + ”,„, ”,„ - ”,„. ”,„ / ” , ":", ";", "=", "?", "@", "_", "~" i punkty kodowe w zakresie od U + 00A0 do U + D7FF, U + E000 do U + FDCF , U + FDF0 do U + FFFD, U + 10000 do U + 1FFFD, U + 20000 do U + 2FFFD, U + 30000 do U + 3FFFD, U + 40000 do U + 4FFFD, U + 50000 do U + 5FFFD, U +60000 do U + 6FFFD, U + 70000 do U + 7FFFD, U + 80000 do U + 8FFFD, U + 90000 do U + 9FFFD, U + A0000 do U + AFFFD, U + B0000 do U + BFFFD, U + C0000 do U + CFFFD, U + D0000 do U + DFFFD, U + E1000 do U + EFFFD, U + F0000 do U + FFFFD, U + 100000 do U + 10FFFD.

Termin „punkty kodowe URL” jest następnie używany w instrukcji:

Jeśli c nie jest punktem kodowym adresu URL, a nie „%”, przeanalizuj błąd.

w kilku częściach algorytmu analizującego, w tym schemacie, autorytecie, ścieżce względnej, zapytaniu i stanach fragmentów: w zasadzie cały adres URL.

Ponadto walidator http://validator.w3.org/ podaje adresy URL podobne "你好"i nie przekazuje adresów URL zawierających znaki takie jak spacje"a b"

Oczywiście, jak wspomniał Stephen C, nie chodzi tylko o znaki, ale także o kontekst: musisz zrozumieć cały algorytm. Ale ponieważ klasa „Punkty kodu URL” jest używana w kluczowych punktach algorytmu, daje to dobre wyobrażenie o tym, czego możesz użyć, czy nie.

Zobacz także: Znaki Unicode w adresach URL

Ciro Santilli
źródło
5

Muszę wybrać znak, aby podzielić adresy URL na ciąg, więc postanowiłem utworzyć listę znaków, których nie mogłem znaleźć w adresie URL:

>>> allowed = "-_.~!*'();:@&=+$,/?%#[]?@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
>>> from string import printable
>>> ''.join(set(printable).difference(set(allowed)))
'`" <\x0b\n\r\x0c\\\t{^}|>'

Możliwe opcje to nowa linia, tabulacja, spacja, ukośnik odwrotny i "<>{}^|. Chyba pójdę ze spacją lub nową linią. :)

Bunyk
źródło
2

Naprawdę nie jest to odpowiedź na twoje pytanie, ale sprawdzenie poprawności adresu URL to naprawdę poważna pita. Prawdopodobnie lepiej sprawdzić poprawność nazwy domeny i pozostaw część zapytania w adresie URL. To jest moje doświadczenie. Możesz również użyć polecenia ping do adresu URL i sprawdzić, czy spowoduje to prawidłową odpowiedź, ale może to być zbyt wiele jak na tak proste zadanie.

Wyrażenia regularne do wykrywania adresów URL są obfite, google :)

ChrisR
źródło
Ta odpowiedź wskazuje, że sprawdzanie poprawności adresów URL nie jest zadaniem wyrażenia regularnego, ale biblioteki specyficznej dla języka / platformy .
DavidRR
0

Wdrażam stary czytnik / pisarz zapytań i odpowiedzi http (0.9, 1.0, 1.1). Żądanie URI to najbardziej problematyczne miejsce.

Nie można tak po prostu używać RFC 1738, 2396 lub 3986. Istnieje wiele starych klientów HTTP i serwerów, które pozwalają na więcej znaków. Więc zrobiłem badania na podstawie przypadkowo opublikowane dzienniki dostępu webserver: "GET URI HTTP/1.0" 200.

Odkryłem, że w URI często używane są następujące niestandardowe znaki:

\ { } < > | ` ^ "

Znaki te zostały opisane w RFC 1738 jako niebezpieczne .

Jeśli chcesz być zgodny ze wszystkimi starymi klientami i serwerami HTTP - musisz zezwolić tym znakom na żądanie URI.

Proszę przeczytać więcej informacji o tych badaniach w http-og .

puchu
źródło
-4

Wymyśliłem kilka wyrażeń regularnych dla PHP, które konwertują adresy URL w tekście na tagi zakotwiczenia. (Najpierw konwertuje wszystkie adresy URL na http: //, a następnie konwertuje wszystkie adresy URL za pomocą https?: // na href = ... linki HTML

$string = preg_replace('/(https?:\/\/)([!#$&-;=?\-\[\]_a-z~%]+)/sim', '<a href="$1$2">$2</a>', preg_replace('/(\s)((www\.)([!#$&-;=?\-\[\]_a-z~%]+))/sim', '$1http://$2', $string) );

przerzucić
źródło
4
-1; poza tym, że oba zawierają w pewnym stopniu adresy URL, nie ma to nic wspólnego z zadanym pytaniem.
Mark Amery