Quais caracteres tornam um URL inválido?

515

Quais caracteres tornam um URL inválido?

Esses URLs são válidos?

  • example.com/file[/].html
  • http://example.com/file[/].html
Boa
fonte
42
Ao validar, você deve sempre "pensar positivo": peça "o que é válido", todo o resto é inválido. Testar contra os (poucos) caracteres válidos é muito mais seguro (e mais fácil!) Do que todos os possíveis inválidos.
Mfx

Respostas:

600

Em geral, os URIs, conforme definidos pela RFC 3986 (consulte a Seção 2: Caracteres ), podem conter qualquer um dos 84 caracteres a seguir:

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

Observe que esta lista não indica onde esses caracteres podem ocorrer no URI.

Qualquer outro caractere precisa ser codificado com a porcentagem de codificação ( %hh). Cada parte do URI possui restrições adicionais sobre quais caracteres precisam ser representados por uma palavra codificada em porcentagem.

quiabo
fonte
31
(claro, a lista de caracteres não estado onde na URI que pode ocorrer)
Eamon Nerbonne
75
Aqui está uma regex que irá determinar se toda a cadeia contém apenas os caracteres acima: / ^ [!? # $ & -; = - [] _ a-z ~] + $ /
Leif Wickland
43
@ techtous, Sim, eu esqueci de permitir "%" caracteres escapados. Deveria ter parecido mais com: /^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ Havia mais alguma coisa que você achou que deveria estar aceitando? (Só para ficar claro, que regex apenas verifica se a cadeia contém caracteres de URL válidos, não se a cadeia contém uma URL bem formado.)
Leif Wickland
12
@ Timwi RFC 3986 diz: "Um octeto codificado em porcentagem é codificado como um trigêmeo de caracteres, consistindo no caractere percentual"% "seguido pelos dois dígitos hexadecimais que representam o valor numérico do octeto". Ele também diz: "Como o caractere de porcentagem ("% ") serve como indicador para octetos codificados em porcentagem, ele deve ser codificado em porcentagem como"% 25 "para que esse octeto seja usado como dados em um URI". Eu li isso dizendo que um "%" só pode aparecer se for seguido por dois dígitos hexadecimais. Como você lê isso?
Leif Wickland
13
@ Weeble Meu regex incluía esses caracteres usando intervalos. Entre e ';' e entre '?' e '[' você encontrará todos os caracteres que não viu.
Leif Wickland
194

Para adicionar alguns esclarecimentos e abordar diretamente a pergunta acima, existem várias classes de caracteres que causam problemas para URLs e URIs.

Existem alguns caracteres que não são permitidos e nunca devem aparecer em um URL / URI, caracteres reservados (descritos abaixo) e outros que podem causar problemas em alguns casos, mas que estão marcados como "imprudentes" ou "não seguros". As explicações sobre por que os caracteres são restritos estão claramente descritas no RFC-1738 (URLs) e no RFC-2396 (URIs). Observe que o RFC-3986 mais recente (atualização para o RFC-1738) define a construção de quais caracteres são permitidos em um determinado contexto, mas as especificações mais antigas oferecem uma descrição mais simples e mais geral de quais caracteres não são permitidos com as regras a seguir.

Caracteres US-ASCII excluídos não permitidos na sintaxe do URI:

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

O caractere "#" é excluído porque é usado para delimitar um URI de um identificador de fragmento. O caractere de porcentagem "%" é excluído porque é usado para a codificação de caracteres de escape. Em outras palavras, "#" e "%" são caracteres reservados que devem ser usados ​​em um contexto específico.

A lista de caracteres imprudentes é permitida, mas pode causar problemas:

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

Caracteres reservados em um componente de consulta e / ou com significado especial em um URI / URL:

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

A classe de sintaxe "reservada" acima se refere aos caracteres permitidos em um URI, mas que podem não ser permitidos em um componente específico da sintaxe genérica de URI. Os caracteres no conjunto "reservado" não são reservados em todos os contextos . O nome do host, por exemplo, pode conter um nome de usuário opcional, podendo ser algo como ftp://user@hostname/onde o caractere '@' tem um significado especial.

Aqui está um exemplo de um URL que possui caracteres inválidos e imprudentes (por exemplo, '$', '[', ']') e deve ser codificado corretamente:

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

Algumas das restrições de caracteres para URIs / URLs dependem da linguagem de programação. Por exemplo, o '|' O caractere (0x7C), embora marcado apenas como "imprudente" na especificação do URI, lançará uma URISyntaxException no construtor Java java.net.URI, portanto, um URL como http://api.google.com/q?exp=a|bnão é permitido e deve ser codificado como http://api.google.com/q?exp=a%7Cbse estivesse usando Java com uma instância de objeto URI.

JasonM1
fonte
2
Resposta excelente e completa, a única a responder diretamente à pergunta real. A seção reservada pode precisar de trabalho, por exemplo, literal ?é muito bom na seção de consulta, mas impossível antes dela, e acho que não @pertence a nenhuma dessas listas. Ah, e em vez de %25na última corda, você não quer dizer %7C?
22813 Bob Stein
1
Obrigado. Boa captura: o% 25 foi um erro de digitação no exemplo. Nota de rodapé adicionada à descrição da sintaxe "reservada" diretamente do RFC-2396.
precisa saber é o seguinte
1
Essa resposta não é ruim , mas existem algumas confusões e erros. Inicialmente, você conflita caracteres não permitidos e reservados (coisas muito diferentes), faz muita distinção entre caracteres "imprudentes" e outros caracteres não permitidos (descartados na RFC 3986 e sintaticamente irrelevantes até na RFC 2396), e apresenta confusamente uma lista de todos os caracteres reservados como a lista reservada "dentro de um componente de consulta" .
Mark Amery
1
Obrigado, não pretendia agrupar os não permitidos e reservados da mesma forma. Atualizado a resposta. As regras do IMHO na RFC-2396, embora mais antigas, sejam mais simples de entender do que as regras atualizadas em 3986. A resposta reflete mais sobre quais caracteres podem ser problemáticos em geral, e não exatamente sobre qual contexto é permitido ou não.
JasonM1
1
É notável que o Tomcat nas versões recentes (7.0.73+, 8.0.39+, 8.5.7+) começou a rejeitar solicitações com caracteres da categoria "imprudente" com erros HTTP 400: "Caractere inválido encontrado no destino da solicitação. caracteres válidos são definidos nas RFC 7230 e RFC 3986 "
Philip
101

A maioria das respostas existentes aqui é impraticável porque ignora totalmente o uso no mundo real de endereços como:

Primeiro, uma digressão na terminologia. Quais são esses endereços? Eles são URLs válidos?

Historicamente, a resposta foi "não". De acordo com a RFC 3986 , a partir de 2005, esses endereços não são URIs (e, portanto, não são URLs, pois os URLs são um tipo de URIs ). De acordo com a terminologia dos padrões IETF de 2005, devemos chamá-los adequadamente de IRIs (Internationalized Resource Identifiers), conforme definido na RFC 3987 , que tecnicamente não são URIs, mas podem ser convertidos em URIs simplesmente codificando por cento todos os caracteres não ASCII do IRI. .

Por especificação moderna, a resposta é "sim". O padrão de vida do WHATWG simplesmente classifica tudo o que anteriormente seria chamado de "URIs" ou "IRIs" como "URLs". Isso alinha a terminologia especificada com a forma como as pessoas normais que não leram a especificação usam a palavra "URL", que era um dos objetivos da especificação .

Quais caracteres são permitidos sob o Padrão de Vida WHATWG?

De acordo com esse novo significado de "URL", quais caracteres são permitidos? Em muitas partes do URL, como a string e o caminho da consulta, podemos usar "unidades de URL" arbitrárias , que são

Pontos de código de URL e bytes codificados em porcentagem .

O que são "pontos de código de URL"?

Os pontos de código da URL são ASCII alfanuméricos, U + 0021 (!), U + 0024 ($), U + 0026 (&), U + 0027 ('), U + 0028 PARENTHESIS ESQUERDO, U + 0029 PARENTHESIS DIREITO, 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 (~) e pontos de código no intervalo U + 00A0 a U + 10FFFD, inclusive, excluindo substitutos e não caracteres.

(Observe que a lista de "pontos de código de URL" não inclui %, mas isso %é permitido em "unidades de código de URL" se fizer parte de uma sequência de codificação percentual.)

O único lugar em que posso identificar onde as especificações permitem o uso de qualquer caractere que não esteja neste conjunto é no host , onde os endereços IPv6 estão entre caracteres [e ]caracteres. Em qualquer outro lugar da URL, unidades de URL são permitidas ou algum conjunto de caracteres ainda mais restritivo.

Quais caracteres foram permitidos sob as antigas RFCs?

Por uma questão de história, e como ela não foi explorada completamente em outras partes das respostas aqui, vamos examinar o que foi permitido no par de especificações mais antigo.

Primeiro de tudo, temos dois tipos de caracteres reservados para RFC 3986 :

  • :/?#[]@, que fazem parte da sintaxe genérica para um URI definido no RFC 3986
  • !$&'()*+,;=, que não fazem parte da sintaxe genérica da RFC, mas são reservados para uso como componentes sintáticos de determinados esquemas de URI. Por exemplo, ponto e vírgula e vírgulas são utilizados como parte da sintaxe dos URIs de dados , e &e =são utilizadas como parte do ubíquo ?foo=bar&qux=bazformato em cadeias de consulta (que não é especificado por RFC 3986).

Qualquer um dos caracteres reservados acima pode ser legalmente usado em um URI sem codificação, para servir ao seu propósito sintático ou apenas como caracteres literais nos dados em alguns lugares onde esse uso não pode ser mal interpretado como o personagem que serve ao seu propósito sintático. (Por exemplo, embora /tenha significado sintático em uma URL, você pode usá-lo não codificado em uma sequência de consultas, porque não possui significado em uma sequência de consultas.)

O RFC 3986 também especifica alguns caracteres não reservados , que sempre podem ser usados ​​simplesmente para representar dados sem nenhuma codificação:

  • abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~

Finalmente, o %próprio personagem é permitido para codificações percentuais.

Isso deixa apenas os seguintes caracteres ASCII que são proibidos de aparecer em um URL:

  • Os caracteres de controle (caracteres 0-1F e 7F), incluindo nova linha, tabulação e retorno de carro.
  • "<>\^`{|}

Todos os outros caracteres do ASCII podem legalmente aparecer em um URL.

Em seguida, o RFC 3987 estende esse conjunto de caracteres não reservados com os seguintes intervalos de caracteres 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

Essas opções de bloco das especificações antigas parecem bizarras e arbitrárias, dadas as mais recentes definições de bloco Unicode ; isso provavelmente ocorre porque os blocos foram adicionados na década desde que o RFC 3987 foi gravado.


Por fim, talvez seja interessante notar que simplesmente saber quais caracteres podem aparecer legalmente em um URL não é suficiente para reconhecer se uma determinada string é um URL legal ou não, pois alguns caracteres são válidos apenas em partes específicas do URL. Por exemplo, os caracteres reservados [e ]são legais como parte de um host literal IPv6 em um URL como http: // [1080 :: 8: 800: 200C: 417A] / foo, mas não são legais em nenhum outro contexto, portanto, o O exemplo do OP http://example.com/file[/].htmlé ilegal.

Mark Amery
fonte
3
plusone para exaustiva referência (por exemplo, RFC)
Yan Foto
19

Na sua pergunta complementar, você perguntou se www.example.com/file[/].htmlé um URL válido.

Esse URL não é válido porque um URL é um tipo de URI e um URI válido deve ter um esquema como http:(consulte a RFC 3986 ).

Se você quis perguntar se http://www.example.com/file[/].htmlé um URL válido, a resposta ainda será não, porque os caracteres de colchete não são válidos lá.

Os caracteres de colchete são reservados para URLs neste formato: http://[2001:db8:85a3::8a2e:370:7334]/foo/bar(ou seja, um literal IPv6 em vez de um nome de host)

Vale a pena ler a RFC 3986 com atenção, se você quiser entender completamente o problema.

Dominic Sayers
fonte
Depois de ler o RFC, estou mais inclinado a concordar com a explicação mais detalhada do @Stephen C.
Skolima
Os URLs não são um subconjunto do URI. O [e ]não são URI válidos para quase analisadores que eu já vi. Isso realmente me ferrou no mundo real: stackoverflow.com/questions/11038967/…
Adam Gent
Os URLs do @AdamGent são um subconjunto de URIs. A única diferença entre eles é se eles descrevem a localização do recurso - que é uma distinção semântica, não sintática. Se os analisadores que você viu que se rotularam como analisadores de "URI" trataram colchetes de maneira diferente daqueles que se identificaram como analisadores de "URL", isso é pura coincidência, não causado por nenhuma diferença entre URLs e URIs.
Mark Amery
@ Mark Amery, é análogo dizer que C ++ é um superconjunto de C. É, na maioria das vezes, mas não totalmente verdadeiro porque (URL e C) é muito mais antigo, pois eles têm que incluir um comportamento menos rigoroso. O problema é que os analisadores de URL analisam coisas que não são válidas para URI ... E eu quero dizer a maioria delas (francamente, estou cansada de apontar isso em muitos idiomas). Não é coincidência a compatibilidade com versões anteriores. Podemos concordar que a especificação de URL seja mais antiga?
Adam Gent
@ MarkAmery Que é de Python, C #, Java e algumas bibliotecas C, os analisadores levarão Unwisemuito a sério os URIs e ainda assim ficarão bem com as bibliotecas de URL. Ou seja, não há sinalizador para ignorar Unwise. Vou ter que verificar o que o Rust lang (já que está sendo construído para um navegador, estou curioso para saber o que faz) dos URLs. A maioria dos navegadores também passará felizmente por "[", "]". Então, em teoria, como eu disse com C / C ++, eles são sub / super, mas a realidade não é tão verdadeira. É altamente dependente da interpretação das especificações e da semântica do super / subconjunto.
Adam Gent
12

Todos os caracteres válidos que podem ser usados ​​em um URI (uma URL é um tipo de URI ) são definidos no RFC 3986 .

Todos os outros caracteres podem ser usados ​​em um URL, desde que sejam "URL codificados" primeiro. Isso envolve a alteração do caractere inválido para "códigos" específicos (geralmente na forma do símbolo de porcentagem (%) seguido por um número hexadecimal).

Este link, referência de codificação de URL HTML , contém uma lista das codificações para caracteres inválidos.

CraigTP
fonte
E para caracteres Unicode , o artigo da Wikipedia Porcentagem de codificação diz o seguinte: "A sintaxe genérica de URI exige que novos esquemas de URI que forneçam a representação de dados de caracteres em um URI, na verdade, representem caracteres do conjunto não reservado sem tradução, e deve converter todos os outros caracteres em bytes de acordo com UTF-8 e, em seguida, codificar em porcentagem esses valores ".
DavidRR
9

Vários intervalos de caracteres Unicode são HTML5 válidos , embora ainda não seja uma boa ideia usá-los.

Por exemplo, os hrefdocumentos dizem http://www.w3.org/TR/html5/links.html#attr-hyperlink-href :

O atributo href nos elementos a e area deve ter um valor que seja um URL válido potencialmente cercado por espaços.

A definição de "URL válido" aponta para http://url.spec.whatwg.org/ , que diz que visa:

Alinhe o RFC 3986 e o ​​RFC 3987 com implementações contemporâneas e obsolete-as no processo.

Esse documento define pontos de código de URL como:

Alfanumérico ASCII, "!", "$", "&", "'", "(", ")", "*", "+", ",", "-" - ",". "," / " , ":", ";", "=", "?", "@", "_", "~" e pontos de código nos intervalos U + 00A0 a U + D7FF, U + E000 a U + FDCF , U + FDF0 a U + FFFD, U + 10000 a U + 1FFFD, U + 20000 a U + 2FFFD, U + 30000 a U + 3FFFD, U + 40000 a U + 4FFFD, U + 50000 a U + 5FFFD, U +60000 para U + 6FFFD, U + 70000 para U + 7FFFD, U + 80000 para U + 8FFFD, U + 90000 para U + 9FFFD, U + A0000 para U + AFFFD, U + B0000 para U + BFFFD, U + C0000 para U + CFFFD, U + D0000 a U + DFFFD, U + E1000 a U + EFFFD, U + F0000 a U + FFFFD, U + 100000 a U + 10FFFD.

O termo "pontos de código de URL" é então usado na declaração:

Se c não for um ponto de código de URL e não "%", analise o erro.

em várias partes do algoritmo de análise, incluindo os estados de esquema, autoridade, caminho relativo, consulta e fragmento: portanto, basicamente, a URL inteira.

Além disso, o validador http://validator.w3.org/ passa por URLs como "你好"e não passa por URLs com caracteres como espaços"a b"

Obviamente, como mencionado por Stephen C, não se trata apenas de caracteres, mas também de contexto: você precisa entender todo o algoritmo. Porém, como a classe "pontos de código de URL" é usada nos pontos principais do algoritmo, é uma boa idéia do que você pode usar ou não.

Consulte também: Caracteres Unicode em URLs

Ciro Santilli adicionou uma nova foto
fonte
5

Eu preciso selecionar o caractere para dividir os URLs em uma string, então decidi criar uma lista de caracteres que não poderiam ser encontrados no URL por mim:

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

Portanto, as opções possíveis são a nova linha, guia, espaço, barra invertida e "<>{}^|. Acho que vou com o espaço ou nova linha. :)

Bunyk
fonte
2

Não é realmente uma resposta para sua pergunta, mas validar URLs é realmente uma pena. Você provavelmente está melhor validando o nome de domínio e deixando a consulta como parte da URL. Essa é a minha experiência. Você também pode recorrer ao ping do URL e ver se ele resulta em uma resposta válida, mas isso pode ser demais para uma tarefa tão simples.

Expressões regulares para detectar URLs são abundantes, pesquise no Google :)

ChrisR
fonte
Esta resposta aconselha que a validação de URL não é um trabalho para uma regex, mas para uma biblioteca específica de idioma / plataforma .
DavidRR
0

Estou implementando o antigo leitor / gravador de solicitações e respostas http (0.9, 1.0, 1.1). Solicitar URI é o local mais problemático.

Você não pode simplesmente usar RFC 1738, 2396 ou 3986 como está. Existem muitos clientes e servidores HTTP antigos que permitem mais caracteres. Então eu fiz a pesquisa com base em logs de acesso webserver acidentalmente publicados: "GET URI HTTP/1.0" 200.

Descobri que os seguintes caracteres não padrão são frequentemente usados ​​no URI:

\ { } < > | ` ^ "

Esses caracteres foram descritos na RFC 1738 como inseguros .

Se você deseja ser compatível com todos os clientes e servidores HTTP antigos - é necessário permitir esses caracteres no URI da solicitação.

Por favor, leia mais informações sobre esta pesquisa em http-og .

puchu
fonte
-4

Eu vim com algumas expressões regulares para PHP que converterão URLs em texto em tags de ancoragem. (Primeiro, converte todos os www. Urls em http: // e depois converte todos os URLs com https?: // para um href = ... html links

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

relipse
fonte
4
-1; além do fato de os dois envolverem URLs de alguma forma, isso não tem nada a ver com a pergunta que foi feita.
Mark Amery