Quali caratteri rendono un URL non valido?


515

Quali caratteri rendono un URL non valido?

Questi URL sono validi?

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

42
Durante la convalida, dovresti sempre "pensare positivo": chiedi "ciò che è valido", tutto il resto non è valido. Testare su (pochi) personaggi validi è molto più sicuro (e più facile!) Di tutti i possibili caratteri non validi.
mfx,

Risposte:


600

In generale, gli URI definiti da RFC 3986 (vedere la Sezione 2: Caratteri ) possono contenere uno dei seguenti 84 caratteri:

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

Si noti che questo elenco non indica dove possono essere presenti questi caratteri nell'URI.

Ogni altro carattere deve essere codificato con la percentuale di codifica ( %hh). Ogni parte dell'URI ha ulteriori restrizioni su ciò che i personaggi devono essere rappresentati da una parola con codifica percentuale.


31
(ovviamente, l'elenco dei personaggi non indica dove si possono verificare
nell'uri

75
Ecco una regex che determinerà se l'intera stringa contiene solo i caratteri sopra: / ^ [! # $ & -; =? - [] _ ​​a-z ~] + $ /
Leif Wickland

43
@techiferous, Sì, ho dimenticato di consentire a "%" caratteri di escape. Avrebbe dovuto assomigliare di più: /^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ c'era qualcos'altro che hai scoperto che avrebbe dovuto accettare? (Per essere chiari, quel regex controlla solo se la stringa contiene caratteri URL validi, non se la stringa contiene un URL ben formato.)
Leif Wickland,

12
@Timwi RFC 3986 dice "Un ottetto con codifica percentuale è codificato come una tripletta di carattere, costituito dal carattere percentuale"% "seguito dalle due cifre esadecimali che rappresentano il valore numerico di quell'ottetto." Dice anche: "Poiché il carattere percentuale ("% ") funge da indicatore per ottetti con codifica percentuale, deve essere codificato con percentuale come"% 25 "affinché quell'ottetto possa essere utilizzato come dati all'interno di un URI." Ho letto che dicendo che un "%" può apparire solo se è seguito da due cifre esadecimali. Come lo leggi?
Leif Wickland,

13
@Weeble Il mio regex includeva quei personaggi usando gli intervalli. Tra e ';' e tra "?" e '[' troverai tutti quei personaggi che non hai visto.
Leif Wickland,

195

Per aggiungere alcuni chiarimenti e rispondere direttamente alla domanda sopra, ci sono diverse classi di caratteri che causano problemi per URL e URI.

Ci sono alcuni caratteri che non sono consentiti e non dovrebbero mai apparire in un URL / URI, caratteri riservati (descritti di seguito) e altri caratteri che possono causare problemi in alcuni casi, ma sono contrassegnati come "poco saggio" o "non sicuro". Le spiegazioni del motivo per cui i caratteri sono limitati sono chiaramente enunciate in RFC-1738 (URL) e RFC-2396 (URI). Nota la più recente RFC-3986 (aggiornamento a RFC-1738) definisce la costruzione di quali caratteri sono ammessi in un determinato contesto, ma le specifiche più vecchie offrono una descrizione più semplice e più generale di quali caratteri non sono ammessi con le seguenti regole.

Caratteri US-ASCII esclusi non consentiti nella sintassi URI:

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

Il carattere "#" è escluso perché viene utilizzato per delimitare un URI da un identificatore di frammento. Il carattere percentuale "%" è escluso perché viene utilizzato per la codifica dei caratteri di escape. In altre parole, "#" e "%" sono caratteri riservati che devono essere utilizzati in un contesto specifico.

L'elenco dei caratteri non saggi è consentito ma può causare problemi:

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

Caratteri che sono riservati all'interno di un componente di query e / o hanno un significato speciale all'interno di un URI / URL:

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

La classe di sintassi "riservata" riportata sopra si riferisce a quei caratteri consentiti all'interno di un URI, ma che potrebbero non essere consentiti all'interno di un particolare componente della sintassi URI generica. I caratteri nel set "riservato" non sono riservati in tutti i contesti . Il nome host, ad esempio, può contenere un nome utente opzionale, quindi potrebbe essere qualcosa di simile al punto in ftp://user@hostname/cui il carattere "@" ha un significato speciale.

Ecco un esempio di un URL che ha caratteri non validi e poco saggi (ad esempio '$', '[', ']') e deve essere codificato correttamente:

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

Alcune delle restrizioni sui caratteri per URI / URL dipendono dal linguaggio di programmazione. Ad esempio, il '|' Il carattere (0x7C) sebbene contrassegnato solo come "non saggio" nelle specifiche URI genererà una URISyntaxException nel costruttore Java java.net.URI, pertanto un URL simile http://api.google.com/q?exp=a|bnon è consentito e deve essere codificato come http://api.google.com/q?exp=a%7Cbse si utilizzasse Java con un'istanza di oggetto URI.


2
Risposta eccellente e completa, l'unica a rispondere direttamente alla domanda reale. La sezione riservata potrebbe richiedere del lavoro, ad esempio letterale ?va bene nella sezione delle query, ma impossibile prima di essa, e non credo che @appartenga a nessuno di questi elenchi. Oh, e invece %25dell'ultima stringa, non intendi %7C?
Bob Stein,

1
Grazie. Buona cattura:% 25 era un errore di battitura nell'esempio. Aggiunta nota a piè di pagina alla descrizione della sintassi "riservata" direttamente da RFC-2396.
JasonM1,

1
Questa risposta non è male , ma ci sono alcune confusioni ed errori. Inizialmente si confondono caratteri non consentiti e riservati (cose molto diverse), si fa troppa distinzione tra caratteri "non saggi" e altri caratteri non consentiti (rilasciati in RFC 3986 e sintatticamente irrilevanti anche in RFC 2396) e si presenta in modo confuso un elenco di tutti i caratteri riservati come l'elenco riservato "all'interno di un componente di query" .
Mark Amery,

1
Grazie, non intendevo raggruppare i non consentiti e riservati come gli stessi. Aggiornato la risposta. Le regole IMHO in RFC-2396 sebbene più vecchie siano più semplici da comprendere rispetto alle regole aggiornate nel 3986. La risposta riflette più su quali caratteri potrebbero essere problematici in generale piuttosto che su quale contesto sia consentito o meno.
JasonM1,

1
È da notare che Tomcat nelle versioni recenti (7.0.73+, 8.0.39+, 8.5.7+) ha iniziato a rifiutare le richieste con caratteri della categoria "non saggia" con errori HTTP 400: "È stato trovato un carattere non valido nella destinazione della richiesta. i caratteri validi sono definiti in RFC 7230 e RFC 3986 "
Philip

101

La maggior parte delle risposte esistenti qui sono poco pratiche perché ignorano totalmente l'uso nel mondo reale di indirizzi come:

Innanzitutto, una digressione nella terminologia. Quali sono questi indirizzi? Sono URL validi?

Storicamente, la risposta è stata "no". Secondo RFC 3986 , dal 2005, tali indirizzi non sono URI (e quindi non URL, poiché gli URL sono un tipo di URI ). Secondo la terminologia degli standard IETF del 2005, dovremmo chiamarli correttamente IRI (Internationalized Resource Identifier), come definito in RFC 3987 , che tecnicamente non sono URI ma possono essere convertiti in URI semplicemente codificando in percentuale tutti i caratteri non ASCII nell'IRI .

Per le specifiche moderne, la risposta è "sì". Il WHATWG Living standard classifica semplicemente tutto ciò che in precedenza si chiamava "URI" o "Iris" come "URL". Ciò allinea la terminologia specificata al modo in cui le persone normali che non hanno letto la specifica usano la parola "URL", che era uno degli obiettivi della specifica .

Quali personaggi sono ammessi ai sensi dello standard di vita WHATWG?

Per questo significato più recente di "URL", quali caratteri sono ammessi? In molte parti dell'URL, come la stringa di query e il percorso, è consentito utilizzare "unità URL" arbitrarie , che sono

Punti del codice URL e byte con codifica percentuale .

Cosa sono i "punti di codice URL"?

I punti del codice URL sono alfanumerici ASCII, U + 0021 (!), U + 0024 ($), U + 0026 (&), U + 0027 ('), U + 0028 PARENTESI SINISTRA, U + 0029 PARENTESI DESTRA, 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 punti di codice nell'intervallo da U + 00A0 a U + 10FFFD, inclusi, esclusi surrogati e non personaggi.

(Si noti che l'elenco di "punti di codice URL" non include %, ma che %sono ammessi in "Unità di codice URL" se fanno parte di una sequenza di codifica percentuale.)

L'unico posto in cui posso individuare dove le specifiche consentono l'uso di qualsiasi carattere che non è in questo set è nell'host , dove sono racchiusi indirizzi IPv6 [e ]caratteri. Ovunque altro nell'URL, sono consentite unità URL o alcuni set di caratteri ancora più restrittivi.

Quali personaggi erano ammessi nei vecchi RFC?

Per il bene della storia, e dal momento che non è esplorato completamente altrove nelle risposte qui, esaminiamo era consentito sotto la vecchia coppia di specifiche.

Innanzitutto, abbiamo due tipi di caratteri riservati RFC 3986 :

  • :/?#[]@, che fanno parte della sintassi generica per un URI definito in RFC 3986
  • !$&'()*+,;=, che non fanno parte della sintassi generica della RFC, ma sono riservati all'uso come componenti sintattici di particolari schemi URI. Ad esempio, virgola e virgole sono usati come parte della sintassi di URI dati , ed &e =sono utilizzati come parte della onnipresente ?foo=bar&qux=bazformato stringhe di query (che non è specificato da RFC 3986).

Qualsiasi dei caratteri riservati sopra può essere legalmente utilizzato in un URI senza codifica, sia per servire il loro scopo sintattico o semplicemente come caratteri letterali nei dati in alcuni luoghi in cui tale uso non potrebbe essere interpretato erroneamente come il personaggio che serve al suo scopo sintattico. (Ad esempio, sebbene /abbia un significato sintattico in un URL, è possibile utilizzarlo non codificato in una stringa di query, poiché non ha significato in una stringa di query.)

RFC 3986 specifica anche alcuni caratteri senza prenotazione , che possono sempre essere utilizzati semplicemente per rappresentare i dati senza alcuna codifica:

  • abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~

Infine, il %carattere stesso è consentito per le codifiche in percentuale.

Ciò lascia solo i seguenti caratteri ASCII a cui è proibito apparire in un URL:

  • I caratteri di controllo (caratteri 0-1F e 7F), inclusi nuova riga, tabulazione e ritorno a capo.
  • "<>\^`{|}

Ogni altro personaggio di ASCII può essere legalmente presente in un URL.

Quindi RFC 3987 estende quella serie di caratteri senza prenotazione con i seguenti intervalli di caratteri 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

Queste scelte di blocco dalle vecchie specifiche sembrano bizzarre e arbitrarie date le ultime definizioni di blocco Unicode ; ciò è probabilmente dovuto al fatto che i blocchi sono stati aggiunti nel decennio da quando è stato scritto RFC 3987.


Infine, forse vale la pena notare che la semplice conoscenza di quali caratteri possono apparire legalmente in un URL non è sufficiente per riconoscere se una determinata stringa è un URL legale o meno, poiché alcuni caratteri sono legali solo in particolari parti dell'URL. Ad esempio, i caratteri riservati [e ]sono legali come parte di un host letterale IPv6 in un URL come http: // [1080 :: 8: 800: 200C: 417A] / pippo ma non sono legali in nessun altro contesto, quindi il L'esempio di OP http://example.com/file[/].htmlè illegale.


3
plusone per riferimenti esaustivi (es. RFC)
Yan Foto

19

Nella tua domanda aggiuntiva hai chiesto se www.example.com/file[/].htmlè un URL valido.

Tale URL non è valido perché un URL è un tipo di URI e un URI valido deve avere uno schema simile http:(vedere RFC 3986 ).

Se intendevi chiedere se http://www.example.com/file[/].htmlè un URL valido, la risposta è ancora no perché i caratteri tra parentesi quadre non sono validi lì.

I caratteri parentesi quadre sono riservati per gli URL in questo formato: http://[2001:db8:85a3::8a2e:370:7334]/foo/bar(ovvero un valore letterale IPv6 anziché un nome host)

Vale la pena leggere attentamente RFC 3986 se si desidera comprendere appieno il problema.


Dopo aver letto la RFC, sono più propenso a concordare con @Stephen C una spiegazione più dettagliata.
skolima,

Un URL non è un sottoinsieme di URI. Gli URI [e ]non sono validi per quasi i parser che ho visto. Questo in realtà mi ha fregato nel mondo reale: stackoverflow.com/questions/11038967/…
Adam Gent,

Gli URL @AdamGent sono un sottoinsieme di URI. L'unica differenza tra loro è se descrivono la posizione della risorsa - che è una distinzione semantica, non sintattica. Se i parser che hai visto etichettati come parser "URI" hanno trattato le parentesi quadre in modo diverso da quelli che si sono etichettati come parser "URL", allora questa è pura coincidenza, non causata da alcuna differenza tra URL e URI.
Mark Amery,

@Mark Amery è analogo al dire che C ++ è un superset di C. È per la maggior parte ma non del tutto vero perché (URL e C) sono molto più vecchi devono includere un comportamento meno rigoroso. Il problema è che i parser di URL analizzeranno cose che non sono URI valide ... E intendo la maggior parte di loro (francamente sono così stanco di indicarlo in così tante lingue) Non è una coincidenza, è compatibilità all'indietro. Possiamo concordare sul fatto che le specifiche dell'URL sono almeno vecchie?
Adam Gent,

@MarkAmery Provenienti da Python, C #, Java e alcune librerie C, i parser prenderanno Unwisemolto sul serio per gli URI e tuttavia andranno bene con le librerie URL. Cioè non c'è bandiera da ignorare Unwise. Dovrò controllare cosa Rust lang (dal momento che è stato creato per un browser, sono curioso di sapere cosa fa) per gli URL. La maggior parte dei browser passerà felicemente anche "[", "]". Quindi in teoria, proprio come ho detto con C / C ++, sono sub / super, ma la realtà non è così vera. Dipende fortemente dall'interpretazione delle specifiche e della semantica del super / sottoinsieme.
Adam Gent,

12

Tutti i caratteri validi che possono essere utilizzati in un URI (un URL è un tipo di URI ) sono definiti in RFC 3986 .

Tutti gli altri caratteri possono essere utilizzati in un URL a condizione che siano prima "URL codificati". Ciò comporta la modifica del carattere non valido per "codici" specifici (di solito sotto forma del simbolo percentuale (%) seguito da un numero esadecimale).

Questo collegamento, HTML Encoding Reference , contiene un elenco delle codifiche per caratteri non validi.


E per i caratteri Unicode , l'articolo di Wikipedia La codifica in percentuale dice quanto segue: "La sintassi generica dell'URI impone che i nuovi schemi URI che prevedono la rappresentazione dei dati dei caratteri in un URI debbano, in effetti, rappresentare i caratteri dell'insieme non prenotato senza traduzione, e dovrebbe convertire tutti gli altri caratteri in byte secondo UTF-8 e quindi codificare in percentuale quei valori . "
DavidRR,

9

Molte gamme di caratteri Unicode sono HTML5 valide , anche se potrebbe non essere una buona idea usarle.

Ad esempio, i hrefdocumenti dicono http://www.w3.org/TR/html5/links.html#attr-hyperlink-href :

L'attributo href sugli elementi a e area deve avere un valore che sia un URL valido potenzialmente circondato da spazi.

Quindi la definizione di "URL valido" punta a http://url.spec.whatwg.org/ , che afferma che mira a:

Allinea RFC 3986 e RFC 3987 con implementazioni contemporanee e obsolete nel processo.

Tale documento definisce i punti del codice URL come:

ASCII alfanumerico, "!", "$", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/" , ":", ";", "=", "?", "@", "_", "~" e punti di codice negli intervalli da 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 Da +60000 a U + 6FFFD, U da + 70000 a U + 7FFFD, U da + 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.

Il termine "punti di codice URL" viene quindi utilizzato nell'istruzione:

Se c non è un punto di codice URL e non "%", analizzare l'errore.

in diverse parti dell'algoritmo di analisi, inclusi lo schema, l'autorità, il percorso relativo, la query e gli stati dei frammenti: quindi sostanzialmente l'intero URL.

Inoltre, il validatore http://validator.w3.org/ passa per URL simili "你好"e non per URL con caratteri come spazi"a b"

Ovviamente, come menzionato da Stephen C, non si tratta solo di personaggi ma anche di contesto: devi capire l'intero algoritmo. Ma poiché la classe "Punti del codice URL" viene utilizzata sui punti chiave dell'algoritmo, ciò fornisce una buona idea di ciò che è possibile utilizzare o meno.

Vedi anche: caratteri Unicode negli URL


5

Devo selezionare il carattere per dividere gli URL nella stringa, quindi ho deciso di creare un elenco di caratteri che non sono stati trovati nell'URL da solo:

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

Quindi, le possibili scelte sono newline, tab, space, backslash e "<>{}^|. Immagino che andrò con lo spazio o la newline. :)


2

Non è davvero una risposta alla tua domanda, ma convalidare l'URL è davvero una pita seria Probabilmente stai solo meglio convalidando il nome di dominio e lasciare che la query sia parte dell'URL. Questa è la mia esperienza. Potresti anche ricorrere al ping dell'URL e vedere se si traduce in una risposta valida, ma potrebbe essere troppo per un'attività così semplice.

Le espressioni regolari per rilevare gli URL sono abbondanti, cercalo su Google :)



Questa risposta indica che la convalida dell'URL è un lavoro non per una regex, ma per una libreria specifica per lingua / piattaforma .
DavidRR,

0

Sto implementando il vecchio lettore / scrittore di richieste e risposte http (0.9, 1.0, 1.1). L'URI di richiesta è il luogo più problematico.

Non puoi semplicemente usare RFC 1738, 2396 o 3986 così com'è. Esistono molti vecchi client e server HTTP che consentono più caratteri. Così ho fatto di ricerca sulla base accidentalmente pubblicati log di accesso server web: "GET URI HTTP/1.0" 200.

Ho scoperto che i seguenti caratteri non standard sono spesso utilizzati nell'URI:

\ { } < > | ` ^ "

Questi caratteri sono stati descritti in RFC 1738 come non sicuri .

Se vuoi essere compatibile con tutti i vecchi client e server HTTP, devi consentire questi caratteri nell'URI di richiesta.

Si prega di leggere ulteriori informazioni su questa ricerca in http-og .


-4

Ho trovato un paio di espressioni regolari per PHP che convertiranno gli URL nel testo in tag di ancoraggio. (Prima converte tutti gli URL www. In http: // quindi converte tutti gli URL con https?: // in un link 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; oltre al fatto che entrambi coinvolgono URL in qualche modo, questo non ha nulla a che fare con la domanda che è stata posta.
Mark Amery,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.