¿Qué caracteres hacen que una URL no sea válida?

515

¿Qué caracteres hacen que una URL no sea válida?

¿Son estas URL válidas?

  • example.com/file[/].html
  • http://example.com/file[/].html
bueno
fuente
42
Al validar, siempre debe "pensar en positivo": preguntar "qué es válido", todo lo demás no es válido. Probar contra los (pocos) caracteres válidos es mucho más seguro (¡y más fácil!) Que todos los posibles inválidos.
mfx

Respuestas:

600

En general, los URI definidos por RFC 3986 (consulte la Sección 2: Caracteres ) pueden contener cualquiera de los siguientes 84 caracteres:

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

Tenga en cuenta que esta lista no indica en qué parte del URI pueden aparecer estos caracteres.

Cualquier otro carácter debe codificarse con el porcentaje de codificación ( %hh). Cada parte del URI tiene más restricciones sobre qué caracteres deben ser representados por una palabra codificada en porcentaje.

Gumbo
fuente
31
(por supuesto, la lista de caracteres no indica en qué lugar de la uri pueden aparecer)
Eamon Nerbonne
75
Aquí hay una expresión regular que determinará si toda la cadena contiene solo los caracteres anteriores: / ^ [! # $ & -; =? - [] _ ​​a-z ~] + $ /
Leif Wickland
43
@techiferous, sí, olvidé permitir que "%" escapen caracteres. Debería haberse parecido más a: /^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ ¿Hubo algo más que haya encontrado que debería haber aceptado? (Solo para ser claros, esa expresión regular solo verifica si la cadena contiene caracteres de URL válidos, no si la cadena contiene una URL bien formada.)
Leif Wickland
12
@Timwi RFC 3986 dice: "Un octeto codificado en porcentaje se codifica como un triplete de caracteres, que consiste en el carácter de porcentaje"% "seguido de los dos dígitos hexadecimales que representan el valor numérico de ese octeto". También dice: "Debido a que el carácter de porcentaje ("% ") sirve como indicador de octetos codificados por porcentaje, debe codificarse como porcentaje" 25% "para que ese octeto se use como datos dentro de un URI". Leí eso diciendo que un "%" puede aparecer solo si es seguido por dos dígitos hexadecimales. ¿Cómo lo lees?
Leif Wickland
13
@Weeble My regex incluyó esos caracteres usando rangos. En medio y ';' y entre '?' y '[' encontrarás todos esos personajes que no viste.
Leif Wickland
195

Para agregar alguna aclaración y abordar directamente la pregunta anterior, hay varias clases de caracteres que causan problemas para las URL y los URI.

Hay algunos caracteres que no están permitidos y que nunca deberían aparecer en una URL / URI, caracteres reservados (descritos a continuación) y otros caracteres que pueden causar problemas en algunos casos, pero están marcados como "imprudentes" o "inseguros". Las explicaciones de por qué los caracteres están restringidos se explican claramente en RFC-1738 (URL) y RFC-2396 (URI). Tenga en cuenta que el RFC-3986 más reciente (actualización de RFC-1738) define la construcción de qué caracteres están permitidos en un contexto dado, pero la especificación anterior ofrece una descripción más simple y más general de los caracteres que no están permitidos con las siguientes reglas.

Caracteres US-ASCII excluidos no permitidos dentro de la sintaxis de URI:

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

El carácter "#" se excluye porque se usa para delimitar un URI de un identificador de fragmento. El carácter de porcentaje "%" se excluye porque se usa para la codificación de caracteres escapados. En otras palabras, el "#" y el "%" son caracteres reservados que deben usarse en un contexto específico.

La lista de caracteres imprudentes está permitida pero puede causar problemas:

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

Caracteres que están reservados dentro de un componente de consulta y / o tienen un significado especial dentro de un URI / URL:

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

La clase de sintaxis "reservada" anterior se refiere a aquellos caracteres que están permitidos dentro de un URI, pero que pueden no estar permitidos dentro de un componente particular de la sintaxis genérica de URI. Los caracteres en el conjunto "reservado" no están reservados en todos los contextos . El nombre de host, por ejemplo, puede contener un nombre de usuario opcional, por lo que podría ser algo así como ftp://user@hostname/donde el carácter '@' tiene un significado especial.

Aquí hay un ejemplo de una URL que tiene caracteres no válidos e imprudentes (por ejemplo, '$', '[', ']') y debe codificarse correctamente:

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

Algunas de las restricciones de caracteres para URI / URL dependen del lenguaje de programación. Por ejemplo, el '|' (0x7C), aunque solo está marcado como "imprudente" en la especificación de URI, arrojará una URISyntaxException en el constructor Java java.net.URI, por lo que una URL como http://api.google.com/q?exp=a|bno está permitida y debe codificarse como http://api.google.com/q?exp=a%7Cbsi estuviera utilizando Java con una instancia de objeto URI.

JasonM1
fuente
2
Excelente y minuciosa respuesta, la única que responde directamente la pregunta real. La sección reservada puede necesitar trabajo, por ejemplo, literal ?está bien en la sección de consulta, pero es imposible antes, y no creo que @pertenezca a ninguna de estas listas. Ah, y en lugar de %25en la última cadena, ¿no quieres decir %7C?
Bob Stein
1
Gracias. Buena captura: el% 25 fue un error tipográfico en el ejemplo. Se agregó una nota a pie de página a la descripción de sintaxis "reservada" directamente desde RFC-2396.
JasonM1
1
Esta respuesta no es mala , pero hay algunas confusiones y errores. Inicialmente combina caracteres no permitidos y reservados (cosas muy diferentes), hace demasiada distinción entre los caracteres "imprudentes" y otros caracteres no permitidos (incluidos en RFC 3986 y sintácticamente irrelevantes incluso en RFC 2396), y presenta una lista confusa de todos los caracteres reservados como la lista reservada "dentro de un componente de consulta" .
Mark Amery
1
Gracias, no quise agrupar a los rechazados y reservados como lo mismo. Se actualizó la respuesta. Las reglas de la OMI en RFC-2396, aunque antiguas, son más fáciles de entender que las reglas actualizadas en 3986. La respuesta refleja más sobre qué caracteres pueden ser problemáticos en general en lugar de exactamente qué contexto está permitido o no permitido.
JasonM1
1
Es notable que Tomcat en versiones recientes (7.0.73+, 8.0.39+, 8.5.7+) haya comenzado a rechazar solicitudes con caracteres de la categoría "imprudente" con errores HTTP 400: "Carácter no válido encontrado en el objetivo de la solicitud. los caracteres válidos se definen en RFC 7230 y RFC 3986 "
Philip
101

La mayoría de las respuestas existentes aquí no son prácticas porque ignoran totalmente el uso de direcciones en el mundo real como:

Primero, una digresión en la terminología. ¿Cuáles son estas direcciones? ¿Son URL válidas?

Históricamente, la respuesta fue "no". Según RFC 3986 , desde 2005, tales direcciones no son URI (y, por lo tanto, no son URL, ya que las URL son un tipo de URI ). Según la terminología de los estándares IETF de 2005, deberíamos llamarlos adecuadamente IRI (Identificadores de recursos internacionalizados), tal como se define en RFC 3987 , que técnicamente no son URI pero pueden convertirse a URI simplemente codificando todos los caracteres no ASCII en el IRI. .

Según las especificaciones modernas, la respuesta es "sí". El WHATWG Living Standard simplemente clasifica todo lo que anteriormente se llamaría "URI" o "IRI" como "URL". Esto alinea la terminología especificada con la forma en que las personas normales que no han leído la especificación usan la palabra "URL", que era uno de los objetivos de la especificación .

¿Qué caracteres están permitidos bajo el WHATWG Living Standard?

Según este nuevo significado de "URL", ¿qué caracteres están permitidos? En muchas partes de la URL, como la cadena de consulta y la ruta, podemos usar "unidades URL" arbitrarias , que son

Puntos de código URL y bytes codificados en porcentaje .

¿Qué son los "puntos de código URL"?

Los puntos del código URL son alfanuméricos ASCII, U + 0021 (!), U + 0024 ($), U + 0026 (&), U + 0027 ('), U + 0028 PARENTESIS IZQUIERDA, U + 0029 PARENTESIS DERECHA, 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 (~) y puntos de código en el rango U + 00A0 a U + 10FFFD, inclusive, excluyendo sustitutos y no personajes.

(Tenga en cuenta que la lista de "puntos de código URL" no incluye %, pero que %se permiten en "unidades de código URL" si son parte de una secuencia de codificación porcentual).

El único lugar que puedo detectar donde la especificación permite el uso de cualquier carácter que no esté en este conjunto es en el host , donde se incluyen las direcciones IPv6 [y los ]caracteres. En cualquier otro lugar de la URL, se permiten unidades de URL o algún conjunto de caracteres aún más restrictivo.

¿Qué caracteres fueron permitidos bajo los viejos RFC?

Por el bien de la historia, y dado que no se explora por completo en ninguna otra parte de las respuestas aquí, examinemos que estaba permitido bajo el par de especificaciones más antiguas.

En primer lugar, tenemos dos tipos de caracteres reservados RFC 3986 :

  • :/?#[]@, que forman parte de la sintaxis genérica para un URI definido en RFC 3986
  • !$&'()*+,;=, que no forman parte de la sintaxis genérica de RFC, pero están reservadas para su uso como componentes sintácticos de esquemas de URI particulares. Por ejemplo, puntos y comas y comas se utilizan como parte de la sintaxis de URIs de datos , y &y =se utilizan como parte de la omnipresente ?foo=bar&qux=bazformato en cadenas de consulta (que no está especificado por el RFC 3986).

Cualquiera de los caracteres reservados arriba se puede usar legalmente en un URI sin codificación, ya sea para cumplir su propósito sintáctico o simplemente como caracteres literales en los datos en algunos lugares donde dicho uso no puede ser malinterpretado como el carácter que cumple su propósito sintáctico. (Por ejemplo, aunque /tiene un significado sintáctico en una URL, puede usarlo sin codificar en una cadena de consulta, porque no tiene significado en una cadena de consulta).

RFC 3986 también especifica algunos caracteres no reservados , que siempre se pueden usar simplemente para representar datos sin ninguna codificación:

  • abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~

Finalmente, el %carácter en sí está permitido para codificaciones porcentuales.

Eso deja solo los siguientes caracteres ASCII que tienen prohibido aparecer en una URL:

  • Los caracteres de control (caracteres 0-1F y 7F), incluyendo nueva línea, tabulación y retorno de carro.
  • "<>\^`{|}

Todos los demás caracteres de ASCII pueden aparecer legalmente en una URL.

Luego, RFC 3987 extiende ese conjunto de caracteres no reservados con los siguientes rangos 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

Estas opciones de bloque de la especificación anterior parecen extrañas y arbitrarias dadas las últimas definiciones de bloque Unicode ; Esto probablemente se deba a que los bloques se han agregado en la década desde que se escribió RFC 3987.


Finalmente, quizás valga la pena señalar que el simple hecho de saber qué caracteres pueden aparecer legalmente en una URL no es suficiente para reconocer si una cadena dada es una URL legal o no, ya que algunos caracteres solo son legales en determinadas partes de la URL. Por ejemplo, los caracteres reservados [y ]son legales como parte de un host literal IPv6 en una URL como http: // [1080 :: 8: 800: 200C: 417A] / foo pero no son legales en ningún otro contexto, por lo que El ejemplo de OP http://example.com/file[/].htmles ilegal.

Mark Amery
fuente
3
plusone para referencias exhaustivas (p. ej., RFC)
Yan Foto
19

En su pregunta complementaria, preguntó si www.example.com/file[/].htmles una URL válida.

Esa URL no es válida porque una URL es un tipo de URI y una URI válida debe tener un esquema como http:(ver RFC 3986 ).

Si pretendía preguntar si http://www.example.com/file[/].htmles una URL válida, entonces la respuesta sigue siendo no porque los caracteres de corchetes no son válidos allí.

Los caracteres de corchete están reservados para las URL en este formato: http://[2001:db8:85a3::8a2e:370:7334]/foo/bar(es decir, un literal IPv6 en lugar de un nombre de host)

Vale la pena leer el RFC 3986 detenidamente si desea comprender el problema por completo.

Dominic Sayers
fuente
Después de leer el RFC, estoy más dispuesto a aceptar una explicación más detallada de @Stephen C.
skolima
Las URL no son un subconjunto de URI. Los [y ]no son URI válidos para casi analizadores que he visto. Esto realmente me ha jodido en el mundo real: stackoverflow.com/questions/11038967/…
Adam Gent
Las URL de @AdamGent son un subconjunto de URI. La única diferencia entre ellos es si describen la ubicación del recurso, que es una distinción semántica, no sintáctica. Si los analizadores que ha visto que se etiquetaron a sí mismos como analizadores "URI" trataron los corchetes de manera diferente a los que se etiquetaron a sí mismos como analizadores "URL", entonces es pura coincidencia, no causada por ninguna diferencia entre URL y URI.
Mark Amery
@ Mark Amery es análogo a decir que C ++ es un superconjunto de C. Es en su mayor parte, pero no del todo cierto, porque (URL y C) son mucho más antiguos, tienen que incluir un comportamiento que sea menos estricto. El problema es que los analizadores de URL analizarán cosas que no son URI válidas ... Y me refiero a la mayoría de ellas (francamente, estoy tan cansado de señalar esto en tantos idiomas). No es casualidad, es compatibilidad con versiones anteriores. ¿Podemos aceptar que la especificación de URL es al menos más antigua?
Adam Gent
@MarkAmery Eso es de Python, C #, Java y algunas bibliotecas de C que los analizadores se tomarán Unwisemuy en serio para los URI y, sin embargo, estarán bien con las bibliotecas de URL. Es decir, no hay bandera para ignorar Unwise. Tendré que verificar qué Rust lang (ya que se está construyendo para un navegador, tengo curiosidad por saber qué hace) para las URL. Sin embargo, la mayoría de los navegadores también pasarán "[", "]". Entonces, en teoría, como dije con C / C ++, son sub / super, pero la realidad no es tan cierta. Depende en gran medida de la interpretación de las especificaciones y la semántica de super / subconjunto.
Adam Gent
12

Todos los caracteres válidos que se pueden usar en un URI (una URL es un tipo de URI ) se definen en RFC 3986 .

Todos los demás caracteres se pueden usar en una URL siempre que primero estén "codificados con URL". Esto implica cambiar el carácter no válido para "códigos" específicos (generalmente en forma de símbolo de porcentaje (%) seguido de un número hexadecimal).

Este enlace, Referencia de codificación de URL HTML , contiene una lista de las codificaciones de caracteres no válidos.

CraigTP
fuente
Y para los caracteres Unicode , el artículo de Wikipedia Codificación porcentual dice lo siguiente: "La sintaxis genérica de URI exige que los nuevos esquemas de URI que proporcionan la representación de datos de caracteres en un URI deben, en efecto, representar caracteres del conjunto no reservado sin traducción, y debe convertir todos los demás caracteres a bytes de acuerdo con UTF-8, y luego codificar en porcentaje esos valores ".
DavidRR
9

Varios de los rangos de caracteres Unicode son HTML5 válidos , aunque aún podría no ser una buena idea usarlos.

Por ejemplo, los hrefdocumentos dicen http://www.w3.org/TR/html5/links.html#attr-hyperlink-href :

El atributo href en un elemento de área debe tener un valor que sea una URL válida potencialmente rodeada de espacios.

Luego, la definición de "URL válida" apunta a http://url.spec.whatwg.org/ , que dice que apunta a:

Alinee RFC 3986 y RFC 3987 con implementaciones contemporáneas y desactívelas en el proceso.

Ese documento define los puntos de código URL como:

ASCII alfanumérico, "!", "$", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/" , ":", ";", "=", "?", "@", "_", "~" y puntos de código en los rangos 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 a U + 6FFFD, U + 70000 a U + 7FFFD, U + 80000 a U + 8FFFD, U + 90000 a U + 9FFFD, U + A0000 a U + AFFFD, U + B0000 a U + BFFFD, U + C0000 a U + CFFFD, U + D0000 a U + DFFFD, U + E1000 a U + EFFFD, U + F0000 a U + FFFFD, U + 100000 a U + 10FFFD.

El término "puntos de código URL" se usa en la declaración:

Si c no es un punto de código URL y no es "%", analice el error.

en varias partes del algoritmo de análisis, incluidos el esquema, la autoridad, la ruta relativa, la consulta y los estados de fragmentos: básicamente, la URL completa.

Además, el validador http://validator.w3.org/ pasa por URL como "你好", y no pasa por URL con caracteres como espacios"a b"

Por supuesto, como mencionó Stephen C, no se trata solo de personajes sino también de contexto: hay que entender todo el algoritmo. Pero dado que la clase "puntos de código URL" se usa en puntos clave del algoritmo, eso da una buena idea de lo que puede usar o no.

Ver también: caracteres Unicode en URL

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
5

Necesito seleccionar un carácter para dividir las URL en una cadena, así que decidí crear una lista de caracteres que yo no podría encontrar en la URL:

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

Por lo tanto, las opciones posibles son la nueva línea, la pestaña, el espacio, la barra diagonal inversa y "<>{}^|. Supongo que iré con el espacio o la nueva línea. :)

Bunyk
fuente
2

Realmente no es una respuesta a su pregunta, pero validar la URL es realmente una pregunta seria. Probablemente sea mejor validar el nombre de dominio y dejar la consulta como parte de la url. Esa es mi experiencia. También podría recurrir a hacer ping a la URL y ver si da como resultado una respuesta válida, pero eso podría ser demasiado para una tarea tan simple.

Las expresiones regulares para detectar las URL son abundantes, google :)

ChrisR
fuente
Esta respuesta informa que la validación de URL es un trabajo no para una expresión regular, sino para una biblioteca específica de idioma / plataforma .
DavidRR
0

Estoy implementando el antiguo lector / escritor de solicitudes y respuestas http (0.9, 1.0, 1.1). Solicitar URI es el lugar más problemático.

No puede usar RFC 1738, 2396 o 3986 tal como está. Hay muchos clientes y servidores HTTP antiguos que permiten más caracteres. Así que he hecho la investigación basada en los registros de acceso del servidor web publicadas por accidente: "GET URI HTTP/1.0" 200.

Descubrí que los siguientes caracteres no estándar a menudo se usan en URI:

\ { } < > | ` ^ "

Estos caracteres fueron descritos en RFC 1738 como inseguros .

Si desea ser compatible con todos los clientes y servidores HTTP antiguos, debe permitir estos caracteres en el URI de solicitud.

Lea más información sobre esta investigación en http-og .

puchu
fuente
-4

Se me ocurrió un par de expresiones regulares para PHP que convertirán las URL en texto en etiquetas de anclaje. (Primero convierte todas las URL de www. A http: // luego convierte todas las URL con https?: // a enlaces 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
fuente
44
-1; más allá del hecho de que ambos involucran URL de alguna manera, esto no tiene nada que ver con la pregunta que se hizo.
Mark Amery