Какие символы делают URL недействительным?

515

Какие символы делают URL недействительным?

Это действительные URL?

  • example.com/file[/].html
  • http://example.com/file[/].html
хорошо
источник
43
При проверке вы всегда должны «думать позитивно»: спросите «что действительно», все остальное недействительно. Тестирование против (нескольких) действительных символов намного безопаснее (и проще!), Чем все возможные недопустимые.
MFX

Ответы:

600

В целом URI, определенные в RFC 3986 (см. Раздел 2: Символы ), могут содержать любой из следующих 84 символов:

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

Обратите внимание, что в этом списке не указано, где в URI могут присутствовать эти символы.

Любой другой символ должен быть закодирован с помощью процентного кодирования ( %hh). Каждая часть URI имеет дополнительные ограничения относительно того, какие символы должны быть представлены словом в кодировке процента.

гумбо
источник
31
(конечно, в списке персонажей не указано, где в uri они могут появляться)
Имон Нербонн
75
Вот регулярное выражение, которое определит, содержит ли вся строка только указанные выше символы: / ^ [! # $ & -; =? - [] _ ​​a-z ~] + $ /
Лейф Уикленд,
43
@techiferous, да, я забыл разрешить экранировать символы "%". Это должно было выглядеть так: /^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ было ли что-то еще, что вы нашли, что он должен был принять? (Просто чтобы прояснить, это регулярное выражение проверяет только, содержит ли строка допустимые символы URL, а не если строка содержит правильно сформированный URL.)
Лейф Викленд
12
@Timwi RFC 3986 гласит: «Октет, закодированный в процентах, кодируется как символьный триплет, состоящий из символа процента«% », за которым следуют две шестнадцатеричные цифры, представляющие числовое значение этого октета». В нем также говорится: «Поскольку символ процента («% ») служит индикатором для октетов, закодированных в процентах, он должен быть закодирован в процентах как«% 25 », чтобы этот октет использовался в качестве данных в URI». Я прочитал это как высказывание, что «%» может появиться, только если за ним следуют две шестнадцатеричные цифры. Как вы это читаете?
Лейф Викленд
13
@Weeble Мой regex включал эти символы, используя диапазоны. Между и ';' и между '?' и '[' вы найдете всех тех персонажей, которых не видели.
Лейф Викленд
195

Чтобы добавить некоторые пояснения и непосредственно обратиться к вышеупомянутому вопросу, есть несколько классов символов, которые вызывают проблемы для 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.

JasonM1
источник
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 "
Филипп
101

Большинство существующих ответов здесь нецелесообразно, потому что они полностью игнорируют реальное использование адресов, таких как:

Во-первых, отступление к терминологии. Что есть эти адреса? Это действительные URL?

Исторически ответ был «нет». Согласно RFC 3986 , с 2005 года такие адреса не являются URI (и, следовательно, не URL-адресами, поскольку URL-адреса являются типом URI). ). Согласно терминологии стандартов IETF 2005 года, мы должны правильно называть их IRI (интернационализированные идентификаторы ресурсов), как определено в RFC 3987 , которые технически не являются URI, но могут быть преобразованы в URI просто путем процентного кодирования всех не-ASCII-символов в IRI ,

Согласно современной спецификации, ответ - «да». WHATWG Living Standard просто классифицирует все , что было ранее называться «URIs» или «ИРИС» , как «URL - адрес». Это выравнивает терминологию specced с тем, как обычные люди, которые не читали спецификацию, используют слово «URL», которое было одним из спецификаций. целей .

Какие персонажи разрешены в соответствии со стандартом жизни WHATWG?

В соответствии с более новым значением «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, но не являются допустимыми в любом другом контексте, поэтому Пример OP http://example.com/file[/].htmlявляется незаконным.

Марк Эмери
источник
3
плюс для исчерпывающих ссылок (например, RFC)
Ян Фото
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.
Марк Амери
@Mark Amery - это аналогично тому, что 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-адресе при условии, что они сначала «закодированы». Это включает в себя изменение недопустимого символа для определенных «кодов» (обычно в форме символа процента (%), за которым следует шестнадцатеричное число).

Эта ссылка, HTML HTML Encoding Reference , содержит список кодировок для недопустимых символов.

CraigTP
источник
А для символов Unicode в статье Википедии « Процентное кодирование» говорится следующее: «Общий синтаксис URI требует, чтобы новые схемы URI, которые обеспечивают представление символьных данных в URI, фактически представляли символы из незарезервированного набора без перевода, и должен преобразовывать все остальные символы в байты в соответствии с UTF-8, а затем кодировать эти значения в процентах . "
DavidRR
9

Некоторые из диапазонов символов Юникода являются допустимыми HTML5 , хотя их использование может быть не очень хорошей идеей.

Например, в hrefдокументах говорится http://www.w3.org/TR/html5/links.html#attr-hyperlink-href :

Атрибут href для элементов a и area должен иметь значение, которое является допустимым 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» используется в ключевых точках алгоритма, он дает хорошее представление о том, что вы можете использовать или нет.

Смотрите также: символы Юникода в URL

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
источник
5

Мне нужно выбрать символ для разделения URL в строке, поэтому я решил создать список символов, который не может быть найден в URL самостоятельно:

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

Итак, возможны следующие варианты: перевод строки, табуляция, пробел, обратный слеш и "<>{}^|. Я думаю, я пойду с пробелом или переводом строки. :)

Bunyk
источник
2

На самом деле это не ответ на ваш вопрос, но проверка URL-адресов - это серьезный вопрос. Вам, вероятно, лучше проверить доменное имя и оставить часть запроса в URL-адресе. Это мой опыт. Вы также можете прибегнуть к проверке URL-адреса и посмотреть, приведет ли он к правильному ответу, но это может быть слишком много для такой простой задачи.

Регулярные выражения для определения URL-адресов в изобилии, Google это :)

ChrisR
источник
Этот ответ говорит о том, что проверка 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 .

puchu
источник
-4

Я придумал пару регулярных выражений для PHP, которые преобразуют URL-адреса в тексте в теги привязки. (Сначала он преобразует все URL-адреса www. В 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) );

relipse
источник
4
-1; Помимо того факта, что они оба включают URL-адреса в той или иной степени, это не имеет ничего общего с заданным вопросом.
Марк Амери