Які символи роблять URL-адресу недійсною?


514

Які символи роблять URL-адресу недійсною?

Це дійсні URL-адреси?

  • example.com/file[/].html
  • http://example.com/file[/].html

42
Під час перевірки слід завжди "думати позитивно": запитувати "що справедливо", все інше недійсне. Тестування на (кількох) дійсних символах набагато безпечніше (і простіше!), Ніж усі можливі недійсні.
mfx

Відповіді:


600

Загалом URI, визначені RFC 3986 (див. Розділ 2: Символи ), можуть містити будь-який із наступних 84 символів:

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

Зауважте, що в цьому списку не вказано, де в URI можуть виникати ці символи.

Будь-який інший символ повинен бути кодований відсотковим кодуванням ( %hh). Кожна частина URI має додаткові обмеження щодо того, які символи мають бути представлені у відсотках закодованим словом.


31
(звичайно, у списку символів не
вказано,

75
Ось регулярний вираз, який визначає, чи містить весь рядок лише символи вище: / ^ [! # $ & -; =? - [] _ ​​a-z ~] + $ /
Leif Wickland

43
@techiferous, Так, я забув дозволити "%" втікаючим символам. Це повинно було виглядати більше так: /^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ Чи було щось інше, що ви виявили, що воно повинно було прийняти? (Просто для того, щоб зрозуміти, що регулярний вираз перевіряє, чи містить рядок дійсні символи URL-адреси, а не, якщо рядок містить добре сформовану URL-адресу.)
Leif Wickland

12
@Timwi RFC 3986 говорить: "Октет, кодований у відсотках, кодується як триплет символів, що складається з відсоткових символів"% ", а потім двох шестинадцяткових цифр, що представляють числове значення цього октету". Він також говорить: "Оскільки символ відсотка ("% ") служить індикатором для кодованих відсотками октетів, він повинен бути кодований у відсотках як"% 25 ", щоб цей октет використовувався як дані в URI". Я прочитав це, як кажучи, що "%" може з'являтися, лише якщо за ним слід дві шістнадцяткові цифри. Як ти це читаєш?
Лейф Вікленд

13
@Weeble Мій регекс включив ці символи, використовуючи діапазони. Між '&' та ';' і між "?" і "[" ви знайдете всіх тих символів, яких ви не бачили.
Лейф Вікленд

193

Щоб додати деякі пояснення та безпосередньо вирішити це питання вище, існує кілька класів символів, які спричиняють проблеми для URL-адрес та URI-адрес.

Є деякі символи, які заборонені і ніколи не повинні відображатися в URL / URI, зарезервовані символи (описані нижче) та інші символи, які можуть викликати проблеми в деяких випадках, але позначені як "нерозумні" або "небезпечні". Пояснення, чому символи обмежені, чітко прописані в RFC-1738 (URL-адреси) та RFC-2396 (URI). Зауважте, що новіший RFC-3986 (оновлення до RFC-1738) визначає побудову того, які символи дозволені в даному контексті, але старша специфікація пропонує більш простий і більш загальний опис, який символів заборонено, за допомогою наступних правил.

Виключені символи US-ASCII, заборонені в синтаксисі URI:

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

Символ "#" виключається, оскільки він використовується для розмежування URI від ідентифікатора фрагмента. Значок "%" у відсотках виключається, оскільки він використовується для кодування уникнутих символів. Іншими словами, "#" і "%" є зарезервованими символами, які повинні використовуватися в конкретному контексті.

Список нерозумних символів дозволений, але може спричинити проблеми:

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

Символи, які зарезервовані в компоненті запиту та / або мають спеціальне значення в URI / URL:

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

Вище "зарезервований" клас синтаксису відноситься до тих символів, які дозволені в URI, але які можуть бути дозволені в певному компоненті загального синтаксису URI. Символи в "зарезервованому" наборі зарезервовані не у всіх контекстах . Наприклад, ім'я хоста може містити необов'язкове ім'я користувача, щоб воно могло бути чимось таким, ftp://user@hostname/де символ "@" має особливе значення.

Ось приклад URL-адреси, що містить недійсні та нерозумні символи (наприклад, '$', '[', ']') і має бути правильно закодована:

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

Деякі обмеження символів для URI / URL-адрес залежать від мови програмування. Наприклад, "|" Символ (0x7C), хоча в специфіці URI лише позначений як "нерозумний", передасть URISyntaxException в конструкторі Java java.net.URI, щоб така URL-адреса http://api.google.com/q?exp=a|bне була дозволена і повинна бути закодована замість цього, як http://api.google.com/q?exp=a%7Cbякщо б використовується Java з екземпляром об'єкта URI.


2
Відмінна, ретельна відповідь, єдина, яка безпосередньо відповість на актуальне питання. Зарезервований розділ може потребувати роботи, наприклад, буквальний ?просто в розділі запитів, але перед ним неможливо, і я не думаю, що він @належить до жодного з цих списків. О, і замість %25останнього рядка, ти це не маєш на увазі %7C?
Боб Штейн

1
Дякую. Хороший улов:% 25 був помилковим прикладом. Додано виноску до "застереженого" опису синтаксису безпосередньо з RFC-2396.
JasonM1

1
Ця відповідь непогана , але є деякі плутанини та помилки. Ви спочатку поєднуєте заборонені та зарезервовані символи (дуже різні речі), ви робите занадто велику різницю між "нерозумними" символами та іншими забороненими символами (випадає в RFC 3986 і синтаксично не має значення навіть у RFC 2396), і ви заплутано представляєте список всі зарезервовані символи як список, зарезервований "у складі запиту" .
Марк Амері

1
Дякую, не означало групувати заборонених і зарезервованих як однакових. Оновлено відповідь. Правила IMHO в RFC-2396, хоча і старші, простіші для розуміння, ніж оновлені правила 3986. Відповідь відображає більше, які символи можуть бути проблемними в цілому, а не саме те, в якому контексті це дозволено чи не дозволено.
JasonM1

1
Примітно, що Tomcat в останніх випусках (7.0.73+, 8.0.39+, 8.5.7+) почав відхиляти запити символів із категорії "нерозумно" з помилками HTTP 400: "Недійсний символ знайдено в цілі запиту. дійсні символи визначені в RFC 7230 та RFC 3986 "
Філіп

100

Більшість наявних відповідей тут недоцільні, оскільки вони повністю ігнорують використання реальних адрес таких адрес:

По-перше, відступ до термінології. Що є ці адреси? Чи дійсні URL-адреси?

Історично відповідь була "ні". За даними RFC 3986 , з 2005 року такі адреси не є URI-адресами (і, отже, не URL-адресами, оскільки URL-адреси є типом URI ). Відповідно до термінології стандартів IETF 2005 року, ми повинні належним чином називати їх IRI (Інтернаціоналізовані ідентифікатори ресурсів), визначені в RFC 3987 , які технічно не є URI, але можуть бути перетворені в URI просто шляхом процентного кодування всіх символів, що не мають ASCII в IRI .

Відповідно до сучасної специфікації, відповідь "так". WHATWG Living Standard просто класифікує все , що було раніше називатися «URIs» або «ІРІС» , як «URL - адреса». Це узгоджує вказану термінологію з тим, як звичайні люди, які не читали специфікацію, використовують слово "URL", що було однією з цілей специфікації .

Які символи дозволені відповідно до життєвого стандарту WHATWG?

Які нові символи "URL" дозволяють використовувати відповідно до цього нового значення "URL"? У багатьох частинах URL-адреси, таких як рядок запиту та шлях, ми можемо використовувати довільні "URL-адреси" , які є

Точки коду URL та байти, кодовані у відсотках .

Що таке "URL-адреси коду"?

В точки URL коду є ASCII алфавітно - цифровий, U + 0021 (!), U + 0024 ($), U + 0026 (&), U + 0027 ( '), U + 0028 дужка, U + 0029 закриває дужка, 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 (~) та кодові точки в діапазоні U + 00A0 до U + 10FFFD, включно, за винятком сурогатів та нехарактерних ознак.

(Зверніть увагу, що список "точок коду URL-адреси" не включає %, але %це дозволено в "одиницях коду URL-адреси", якщо вони є частиною послідовності кодування відсотків.)

Єдине місце, де я можу помітити, де специфікація дозволяє використовувати будь-який символ, якого немає в цьому наборі, - це хост , де IPv6 адреси вкладені [та ]символи. Повсюдно в URL-адресі дозволені або блоки URL-адрес, або деякий, навіть більш обмежений набір символів.

Які символи були дозволені за старими RFC?

Заради історії, і оскільки це не досліджено повністю в інших місцях у відповідях тут, давайте розглянемо, як було дозволено за старішою парою специфікацій.

Перш за все, у нас є два типи зарезервованих символів RFC 3986 :

  • :/?#[]@, які є частиною загального синтаксису для URI, визначеного в RFC 3986
  • !$&'()*+,;=, які не входять до загального синтаксису RFC, але зарезервовані для використання як синтаксичні компоненти конкретних схем URI. Наприклад, точка з коми і коми використовуються в якості частини синтаксису URI , даних , а також &і =використовуються в якості частини повсюдного ?foo=bar&qux=bazформату в рядках запиту (який НЕ вказаний в RFC 3986).

Будь-який із вищезазначених зарезервованих символів може бути законно використаний в URI без кодування, або для того, щоб служити їх синтаксичному призначенню, або так само як буквальні символи в даних, де таке використання не може бути неправильно інтерпретоване як символ, що обслуговує його синтаксичне призначення. (Наприклад, хоча /в URL-адресі є синтаксичне значення, ви можете використовувати його некодованим у рядку запиту, оскільки він не має значення в рядку запиту.)

RFC 3986 також визначає деякі незарезервовані символи, які завжди можна використовувати просто для представлення даних без будь-якого кодування:

  • abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~

Нарешті, сам %персонаж дозволений для кодування відсотків.

Це залишає лише такі символи ASCII, яким заборонено з’являтись у URL-адресі:

  • Контрольні символи (символи 0-1F та 7F), включаючи нову лінію, вкладку та повернення каретки.
  • "<>\^`{|}

Кожен інший символ із ASCII може юридично містити URL-адресу.

Тоді RFC 3987 розширює цей набір незарезервованих символів на такі діапазони символів унікоду:

  %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

Цей вибір блоку зі старої специфікації здається химерним та довільним, враховуючи останні визначення блоку Unicode ; це, мабуть, тому, що блоки були додані в десятиліття з моменту написання RFC 3987.


Нарешті, можливо, варто відзначити, що просто знати, які символи можуть легально відображатися в URL-адресі, недостатньо, щоб визнати, чи є певна рядок законною URL-адресою чи ні, оскільки деякі символи є легальними лише в окремих частинах URL-адреси. Наприклад, зарезервовані символи [та ]є легальними як частина прямого хоста IPv6 у URL-адресі, як http: // [1080 :: 8: 800: 200C: 417A] / foo, але не є законними в будь-якому іншому контексті, тому Приклад ОП - http://example.com/file[/].htmlце незаконне.


3
plusone для вичерпних посилань (наприклад, RFC)
Yan Foto

19

У своєму додатковому запитанні ви запитали, чи www.example.com/file[/].htmlдійсна URL-адреса.

Ця URL-адреса недійсна, оскільки URL-адреса є типом URI, і дійсний URI повинен мати схему типу http:(див. RFC 3986 ).

Якщо ви хотіли запитати, чи http://www.example.com/file[/].htmlє дійсною URL-адресою, відповідь все ще ні, тому що символи квадратних дужок не дійсні.

Символи квадратної дужки зарезервовані для URL-адрес у такому форматі: http://[2001:db8:85a3::8a2e:370:7334]/foo/bar(тобто літерал IPv6 замість імені хоста)

Варто уважно прочитати RFC 3986, якщо ви хочете зрозуміти проблему повністю.


Прочитавши RFC, я більше схильний погодитися з @Stephen C з більш детальним поясненням.
Сколіма

URL-адреси не є підмножиною URI. [І ]НЕ URI дійсні в протягом майже аналізаторів , які я бачив. Це насправді накрутило мене в реальному світі: stackoverflow.com/questions/11038967/…
Адам Гент

URL-адреси @AdamGent дуже багато - це підмножина URI. Єдина відмінність між ними полягає в тому, чи описують вони розташування ресурсу - це семантичне відмінність, а не синтаксичне. Якщо парсери, які ви бачили, що позначили себе аналізаторами "URI", трактували квадратні дужки по-різному, ніж ті, що називали себе "URL" аналізаторами, то це чистий збіг, не викликаний різницею між URL-адресами та URI.
Марк Амері

@ Марк Амеррі аналогічно тому, що C ++ - це суперкомплект C. Це здебільшого, але не зовсім вірно, оскільки (URL і C) набагато старші, вони повинні включати поведінку, яка менш сувора. Проблема полягає в тому, що URL-аналізатори розберуть речі, які не відповідають дійсності URI ... І я маю на увазі більшість із них (відверто кажучи, мені так набридло вказувати це на стільки мов). Не випадково це зворотна сумісність. Чи можемо ми погодитися, що специфікація URL старіша принаймні?
Адам Ґент

@MarkAmery. Це з бібліотек Python, C #, Java та деяких C, парсери Unwiseдуже серйозно сприйматимуть URI, але все-таки будуть добре працювати з бібліотеками URL. Тобто немає прапора, який ігнорувати Unwise. Мені доведеться перевірити, що таке Rust lang (оскільки він створюється для браузера, мені цікаво, що він робить) для URL-адрес. Однак більшість браузерів із задоволенням також передасть "[", "]". Тож теоретично, як я вже сказав із C / C ++, вони є суб / супер, але реальність не така правда. Це сильно залежить від інтерпретації специфіки та семантики супер / підмножини.
Адам Гент

12

Усі дійсні символи, які можна використовувати в URI ( URL-адреса - це тип URI ), визначені в RFC 3986 .

Усі інші символи можуть бути використані в URL-адресі за умови, що вони спочатку "кодовані URL-адресою". Це передбачає зміну недійсного символу для конкретних "кодів" (як правило, у вигляді відсотка символу (%) з наступним шістнадцятковим числом).

Це посилання, HTML Посилання на кодування URL-адреси , містить список кодувань для недійсних символів.


А для символів Unicode у статті Вікіпедії " Процент кодування" йдеться про таке: "Загальний синтаксис URI передбачає, що нові схеми URI, які передбачають представлення символьних даних в URI, по суті повинні представляти символи з незарезервованого набору без перекладу, і повинен перетворити всі інші символи в байти відповідно до UTF-8, а потім відсотковим кодувати ці значення . "
DavidRR

9

Кілька діапазонів символів Unicode є дійсним HTML5 , хоча, можливо, все-таки не дуже корисно їх використовувати.

Наприклад, hrefдокументи кажуть http://www.w3.org/TR/html5/links.html#attr-hyperlink-href :

Атрибут href для елементів а та області повинен мати значення, яке є дійсною URL-адресою, потенційно оточеною пробілами.

Тоді визначення "дійсної URL-адреси" вказує на http://url.spec.whatwg.org/ , де зазначено, що воно спрямоване на:

Вирівняйте RFC 3986 та RFC 3987 із сучасними реалізаціями та застарійте їх у процесі.

Цей документ визначає URL-адреси коду як:

Буквено-цифрові ASCII, "!", "$", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/" , ":", ";", "=", "?", "@", "_", "~" та кодові точки в діапазонах U + 00A0 до U + D7FF, U + E000 до U + FDCF , U + FDF0 до U + FFFD, U + 10000 до U + 1FFFD, U + 20000 до U + 2FFFD, U + 30000 до U + 3FFFD, U + 40000 до U + 4FFFD, U + 50000 до U + 5FFFD, U +60000 до U + 6FFFD, U + 70000 до U + 7FFFD, U + 80000 до U + 8FFFD, U + 90000 до U + 9FFFD, U + A0000 до U + AFFFD, U + B0000 до U + BFFFD, U + C0000 до U + CFFFD, U + D0000 до U + DFFFD, U + E1000 до U + EFFFD, U + F0000 до U + FFFFD, U + 100000 до U + 10FFFD.

Термін "Точки коду URL-адреси" потім використовується у виписці:

Якщо c не є кодовою точкою URL-адреси та не "%", помилка розбору.

у кількох частинах алгоритму розбору, включаючи схему, повноваження, відносний шлях, запити та фрагменти: так в основному вся URL-адреса.

Також валідатор http://validator.w3.org/ передає такі URL-адреси, як "你好"і не передає URL-адреси з символами, як пробіли."a b"

Звичайно, як згадував Стівен С, мова йде не лише про символи, а й про контекст: ви повинні розуміти весь алгоритм. Але оскільки клас "URL-адреси коду" використовується в ключових точках алгоритму, він дає гарне уявлення про те, що ви можете використовувати чи ні.

Дивіться також: символи Unicode в URL-адресах


5

Мені потрібно вибрати символ, щоб розділити URL-адреси в рядку, тому я вирішив створити список символів, які не вдалося знайти в URL-адресі самостійно:

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

Отже, можливими варіантами є новий рядок, вкладка, пробіл, зворотний кут і "<>{}^|. Я думаю, я піду пробілом або новим рядком. :)


2

Насправді не є відповіддю на ваше запитання, але перевірка URL-адреси - це справді серйозна піта. Ви, мабуть, просто краще перевірити доменне ім’я та залишити запит частиною URL-адреси. Це мій досвід. Ви також можете вдатися до пінг-адреси URL-адреси і побачити, чи це призводить до правильної відповіді, але це може бути занадто багато для такої простої задачі.

Регулярні вирази для виявлення URL-адрес є рясними, google it :)



Ця відповідь говорить про те, що перевірка URL-адреси - це завдання не для регулярного вираження, а для бібліотеки, орієнтованої на мову / платформу .
DavidRR

0

Я реалізую старий http (0.9, 1.0, 1.1) запит і відповідь читача / запису. Запити URI - найбільш проблемне місце.

Ви не можете просто використовувати RFC 1738, 2396 або 3986 як є. Існує багато старих клієнтів і серверів HTTP, що дозволяє отримати більше символів. Таким чином , я зробив дослідження на основі випадково опублікованих журналів доступу веб - сервера: "GET URI HTTP/1.0" 200.

Я виявив, що в URI часто використовуються такі нестандартні символи:

\ { } < > | ` ^ "

Ці символи були описані в RFC 1738 як небезпечні .

Якщо ви хочете бути сумісними зі всіма старими клієнтами та серверами HTTP - ви повинні дозволити цим символам у URI запиту.

Детальніше про це дослідження читайте на http-og .


-4

Я придумав пару регулярних виразів для PHP, які перетворять URL-адреси в тексті в якірні теги. (Спочатку він перетворює всі www. Urls в http: // потім перетворює всі URL-адреси з https?: // в href = ... html-посилання

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


4
-1; окрім того, що вони обидва залучають URL-адреси в певній якості, це не має нічого спільного з питанням, яке було задано.
Марк Амері
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.