Karakter mana yang membuat URL tidak valid?

515

Karakter mana yang membuat URL tidak valid?

Apakah ini URL yang valid?

  • example.com/file[/].html
  • http://example.com/file[/].html
baik
sumber
42
Saat memvalidasi, Anda harus selalu "berpikir positif": meminta "apa yang valid", semua yang lain tidak valid. Pengujian terhadap (beberapa) karakter yang valid jauh lebih aman (dan lebih mudah!) Daripada semua yang mungkin tidak valid.
mfx

Jawaban:

600

Secara umum URI seperti yang didefinisikan oleh RFC 3986 (lihat Bagian 2: Karakter ) dapat berisi salah satu dari 84 karakter berikut:

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

Perhatikan bahwa daftar ini tidak menyatakan di mana dalam URI karakter ini dapat terjadi.

Karakter lain apa pun harus dikodekan dengan persen-penyandian ( %hh). Setiap bagian dari URI memiliki batasan lebih lanjut tentang karakter apa yang perlu diwakili oleh kata yang dikodekan persen.

Gumbo
sumber
31
(tentu saja, daftar karakter tidak menyatakan di mana di uri mereka dapat terjadi)
Eamon Nerbonne
75
Berikut ini adalah regex yang akan menentukan apakah seluruh string hanya berisi karakter di atas: / ^ [! # $ & -; =? - [] _ ​​a-z ~] + $ /
Leif Wickland
43
@ techiferous, Ya, saya lupa mengizinkan "%" lolos dari karakter. Seharusnya terlihat lebih seperti: /^([!#$&-;=?-[]_a-z~]|%[0-9a-fA-F]{2})+$/ Apakah ada hal lain yang Anda temukan seharusnya sudah diterima? (Hanya untuk memperjelas, regex itu hanya memeriksa apakah string berisi karakter URL yang valid, bukan jika string tersebut berisi URL yang terbentuk dengan baik.)
Leif Wickland
12
@Timwi RFC 3986 mengatakan, "Oktet yang dikodekan persen disandikan sebagai triplet karakter, yang terdiri dari persen karakter"% "diikuti oleh dua digit heksadesimal yang mewakili nilai numerik oktet itu." Ia juga mengatakan, "Karena karakter persen ("% ") berfungsi sebagai indikator untuk oktet yang dikodekan persen, itu harus dikodekan persen sebagai"% 25 "agar oktet itu digunakan sebagai data dalam URI." Saya membaca bahwa mengatakan "%" hanya dapat muncul jika diikuti oleh dua digit hex. Bagaimana Anda membacanya?
Leif Wickland
13
@ Weeble Regex saya menyertakan karakter tersebut dengan menggunakan rentang. Diantara dan ';' dan antara '?' dan '[' Anda akan menemukan semua karakter yang tidak Anda lihat.
Leif Wickland
195

Untuk menambahkan beberapa klarifikasi dan langsung menjawab pertanyaan di atas, ada beberapa kelas karakter yang menyebabkan masalah untuk URL dan URI.

Ada beberapa karakter yang dilarang dan tidak boleh muncul di URL / URI, karakter yang dipesan (dijelaskan di bawah), dan karakter lain yang dapat menyebabkan masalah dalam beberapa kasus, tetapi ditandai sebagai "tidak bijaksana" atau "tidak aman". Penjelasan mengapa karakter dibatasi jelas dijabarkan dalam RFC-1738 (URL) dan RFC-2396 (URI). Perhatikan bahwa RFC-3986 yang lebih baru (pembaruan ke RFC-1738) mendefinisikan konstruksi karakter apa yang diperbolehkan dalam konteks tertentu, tetapi spesifikasi yang lebih lama menawarkan deskripsi yang lebih sederhana dan lebih umum tentang karakter yang tidak diizinkan dengan aturan berikut.

Karakter US-ASCII yang dikecualikan yang tidak diizinkan dalam sintaksis URI:

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

Karakter "#" dikecualikan karena digunakan untuk membatasi URI dari pengidentifikasi fragmen. Persentase karakter "%" dikecualikan karena digunakan untuk penyandian karakter yang diloloskan. Dengan kata lain, "#" dan "%" adalah karakter khusus yang harus digunakan dalam konteks tertentu.

Daftar karakter tidak bijaksana diperbolehkan tetapi dapat menyebabkan masalah:

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

Karakter yang dicadangkan dalam komponen permintaan dan / atau memiliki makna khusus dalam URI / URL:

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

Kelas sintaks "dilindungi" di atas mengacu pada karakter-karakter yang diizinkan dalam URI, tetapi yang mungkin tidak diizinkan dalam komponen tertentu dari sintaksis URI generik. Karakter dalam set "dilindungi undang-undang" tidak dicadangkan di semua konteks . Nama host, misalnya, dapat berisi nama pengguna opsional sehingga bisa berupa sesuatu di ftp://user@hostname/mana karakter '@' memiliki arti khusus.

Berikut adalah contoh URL yang memiliki karakter tidak valid dan tidak bijaksana (mis. '$', '[', ']') Dan harus dikodekan dengan benar:

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

Beberapa batasan karakter untuk URI / URL tergantung pada bahasa pemrograman. Misalnya, '|' (0x7C) karakter meskipun hanya ditandai sebagai "tidak bijaksana" dalam spesifikasi URI akan melempar URISyntaxException di konstruktor Java java.net.URI sehingga URL suka http://api.google.com/q?exp=a|btidak diperbolehkan dan harus dikodekan sebagai gantinya http://api.google.com/q?exp=a%7Cbmenggunakan Java dengan objek objek URI.

JasonM1
sumber
2
Luar biasa, jawaban menyeluruh, satu-satunya yang langsung menjawab pertanyaan yang sebenarnya. Bagian yang dicadangkan mungkin perlu dikerjakan, misal literal boleh ?saja di bagian permintaan, tetapi tidak mungkin sebelumnya, dan saya pikir tidak @termasuk dalam salah satu dari daftar ini. Oh, dan bukannya %25di string terakhir, bukankah maksud Anda %7C?
Bob Stein
1
Terima kasih. Hasil tangkapan yang bagus:% 25 adalah salah ketik pada contoh. Menambahkan catatan kaki ke deskripsi sintaks "milik" langsung dari RFC-2396.
JasonM1
1
Jawaban ini tidak buruk , tetapi ada beberapa kebingungan dan kesalahan. Anda awalnya mengonfigurasikan karakter yang dilarang dan yang disediakan (hal-hal yang sangat berbeda), Anda membuat terlalu banyak perbedaan antara karakter "tidak bijaksana" dan karakter yang tidak diizinkan lainnya (dijatuhkan dalam RFC 3986 dan secara sintaksis tidak relevan bahkan dalam RFC 2396), dan Anda secara membingungkan menyajikan daftar semua karakter yang dipesan sebagai daftar disediakan "dalam komponen permintaan" .
Mark Amery
1
Terima kasih, tidak bermaksud mengelompokkan yang dilarang dan dicadangkan sebagai yang sama. Diperbarui jawabannya. Aturan IMHO di RFC-2396 meskipun lebih tua lebih mudah dipahami daripada aturan yang diperbarui pada 3986. Jawaban lebih mencerminkan karakter mana yang mungkin menyusahkan secara umum daripada konteks mana yang diizinkan atau tidak diizinkan.
JasonM1
1
Perlu dicatat bahwa Tomcat dalam rilis terbaru (7.0.73+, 8.0.39+, 8.5.7+) telah mulai menolak permintaan dengan karakter dari kategori "tidak bijaksana" dengan kesalahan HTTP 400: "Karakter tidak valid ditemukan di target permintaan. The karakter yang valid didefinisikan dalam RFC 7230 dan RFC 3986 "
Philip
101

Sebagian besar jawaban yang ada di sini tidak praktis karena sama sekali mengabaikan penggunaan alamat dunia nyata seperti:

Pertama, penyimpangan ke dalam terminologi. Apa yang alamat ini? Apakah itu URL yang valid?

Secara historis, jawabannya adalah "tidak". Menurut RFC 3986 , sejak 2005, alamat tersebut bukan URI (dan karena itu bukan URL, karena URL adalah jenis URI ). Sesuai dengan terminologi standar IETF 2005, kita harus menyebutnya IRI (Internationalized Resource Identifiers), sebagaimana didefinisikan dalam RFC 3987 , yang secara teknis bukan URI tetapi dapat dikonversi ke URI hanya dengan persen-pengkodean semua karakter non-ASCII dalam IRI .

Per spec modern, jawabannya adalah "ya". The WHATWG Living Standard hanya mengklasifikasikan segala sesuatu yang sebelumnya disebut "URI" atau "IRI" sebagai "URL". Ini disejajarkan dengan specced terminologi dengan cara orang normal yang belum membaca spec penggunaan kata "URL", yang merupakan salah satu spec gol .

Karakter apa yang diizinkan berdasarkan Standar Hidup WHATWG?

Per makna "URL" yang lebih baru ini, karakter apa yang diizinkan? Dalam banyak bagian dari URL, seperti string dan jalan, kita diperbolehkan untuk menggunakan sewenang-wenang "unit URL" , yang

Poin kode URL dan byte yang dikodekan oleh persen .

Apa itu "poin kode URL"?

The poin kode URL yang ASCII alfanumerik, U + 0021 (!), U + 0024 ($), U + 0026 (&), U + 0027 ( '), U + 0028 KIRI kurung, U + 0029 KANAN kurung, 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 (~), dan titik kode dalam kisaran U + 00A0 ke U + 10FFFD, inklusif, tidak termasuk pengganti dan karakter bukan.

(Perhatikan bahwa daftar "titik kode URL" tidak termasuk %, tetapi itu %diizinkan di "unit kode URL" jika mereka bagian dari urutan penyandian persen.)

Satu-satunya tempat saya bisa melihat di mana spec memungkinkan penggunaan karakter apa pun yang tidak ada dalam set ini adalah di host , di mana alamat IPv6 tertutup [dan ]karakter. Di tempat lain di URL, unit URL diizinkan atau beberapa set karakter yang lebih ketat.

Karakter apa yang diizinkan di bawah RFC lama?

Demi sejarah, dan karena itu tidak dieksplorasi sepenuhnya di tempat lain dalam jawaban di sini, mari kita periksa diizinkan di bawah pasangan spesifikasi yang lebih tua.

Pertama-tama, kami memiliki dua jenis karakter khusus RFC 3986 :

  • :/?#[]@, yang merupakan bagian dari sintaksis generik untuk URI yang ditentukan dalam RFC 3986
  • !$&'()*+,;=, yang bukan bagian dari sintaksis umum RFC, tetapi dicadangkan untuk digunakan sebagai komponen sintaksis dari skema URI tertentu. Misalnya, titik koma dan koma digunakan sebagai bagian dari sintaks URI data , dan &dan =digunakan sebagai bagian dari mana-mana ?foo=bar&qux=bazformat string query (yang tidak ditentukan oleh RFC 3986).

Setiap karakter yang dilindungi di atas dapat digunakan secara legal dalam URI tanpa penyandian, baik untuk melayani tujuan sintaksisnya atau hanya sebagai karakter literal dalam data di beberapa tempat di mana penggunaan tersebut tidak dapat disalahartikan sebagai karakter yang melayani tujuan sintaksisnya. (Misalnya, meskipun /memiliki makna sintaksis dalam URL, Anda dapat menggunakannya tanpa enkripsi dalam string kueri, karena itu tidak memiliki makna dalam string kueri.)

RFC 3986 juga menentukan beberapa karakter tanpa pagu harga , yang selalu dapat digunakan hanya untuk merepresentasikan data tanpa pengkodean apa pun:

  • abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~

Akhirnya, %karakter itu sendiri diperbolehkan untuk penyandian-persen.

Yang tersisa hanya karakter ASCII berikut yang dilarang muncul di URL:

  • Karakter kontrol (karakter 0-1F dan 7F), termasuk baris baru, tab, dan carriage return.
  • "<>\^`{|}

Setiap karakter lain dari ASCII dapat secara hukum ditampilkan dalam URL.

Kemudian RFC 3987 memperluas rangkaian karakter yang tidak diawetkan dengan rentang karakter unicode berikut:

  %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

Pilihan blok ini dari spec lama tampak aneh dan sewenang-wenang diberikan definisi blok Unicode terbaru ; ini mungkin karena blok telah ditambahkan pada dekade sejak RFC 3987 ditulis.


Terakhir, mungkin perlu dicatat bahwa mengetahui karakter mana saja yang dapat muncul secara legal di URL tidak cukup untuk mengenali apakah beberapa string yang diberikan adalah URL yang legal atau tidak, karena beberapa karakter hanya legal di bagian-bagian tertentu dari URL. Sebagai contoh, karakter yang dilindungi undang-undang [dan ]legal sebagai bagian dari host literal IPv6 dalam URL seperti http: // [1080 :: 8: 800: 200C: 417A] / foo tetapi tidak legal dalam konteks lain, jadi Contoh OP http://example.com/file[/].htmladalah ilegal.

Mark Amery
sumber
3
plusone untuk referensi lengkap (mis., RFC)
Yan Foto
19

Dalam pertanyaan tambahan, Anda bertanya apakah www.example.com/file[/].htmlURL yang valid.

URL itu tidak valid karena URL adalah jenis URI dan URI yang valid harus memiliki skema seperti http:(lihat RFC 3986 ).

Jika Anda bermaksud menanyakan apakah http://www.example.com/file[/].htmlURL yang valid maka jawabannya masih tidak karena karakter kurung siku tidak valid di sana.

Karakter kurung siku dicadangkan untuk URL dalam format ini: http://[2001:db8:85a3::8a2e:370:7334]/foo/bar(yaitu literal IPv6 alih-alih nama host)

Sebaiknya baca RFC 3986 dengan cermat jika Anda ingin memahami masalah ini sepenuhnya.

Dominic Sayers
sumber
Setelah membaca RFC, saya lebih cenderung setuju dengan @Stephen C penjelasan lebih rinci
skolima
URL bukan subset dari URI. The [dan ]tidak URI berlaku untuk hampir parser saya telah melihat. Ini sebenarnya telah mengacaukan saya di dunia nyata: stackoverflow.com/questions/11038967/...
Adam Gent
@AdamGent URLs sangat banyak merupakan bagian dari URI. Satu-satunya perbedaan di antara mereka adalah apakah mereka menggambarkan lokasi sumber daya - yang merupakan perbedaan semantik, bukan sintaksis. Jika parser yang Anda lihat yang melabeli diri mereka sebagai parser "URI" memperlakukan tanda kurung siku berbeda dengan parser yang menyebut diri mereka sebagai parser "URL", maka itu adalah kebetulan murni, bukan disebabkan oleh perbedaan antara URL dan URI.
Mark Amery
@ Markus Amery analog dengan mengatakan C ++ adalah superset dari C. Ini sebagian besar tetapi tidak sepenuhnya benar karena (URL dan C) jauh lebih tua mereka harus memasukkan perilaku yang kurang ketat. Masalahnya adalah parser URL akan mem-parsing hal-hal yang tidak valid URI ... Dan maksud saya sebagian besar dari mereka (terus terang saya sangat lelah menunjukkan hal ini di banyak bahasa) Bukan kebetulan itu kompatibilitas ke belakang. Bisakah kita setuju bahwa spesifikasi URL minimal lebih lama?
Adam Gent
@MarkAmery Yaitu dari Python, C #, Java dan beberapa pustaka C, parser akan menganggap Unwiseserius URI dan tidak masalah dengan pustaka URL. Itu tidak ada bendera untuk diabaikan Unwise. Saya harus memeriksa apa yang dilakukan Rust lang (karena sedang dibangun untuk browser, saya ingin tahu apa fungsinya) untuk URL. Sebagian besar browser dengan senang hati akan meneruskan "[", "]" juga. Jadi secara teori seperti yang saya katakan dengan C / C ++ mereka sub / super tetapi kenyataannya tidak begitu benar. Ini sangat tergantung pada interpretasi spesifikasi dan semantik super / subset.
Adam Gent
12

Semua karakter yang valid yang dapat digunakan dalam URI ( URL adalah jenis URI ) didefinisikan dalam RFC 3986 .

Semua karakter lain dapat digunakan dalam URL asalkan mereka "URL Dikodekan" terlebih dahulu. Ini melibatkan mengubah karakter yang tidak valid untuk "kode" tertentu (biasanya dalam bentuk simbol persen (%) diikuti oleh angka heksadesimal).

Tautan ini, Referensi Penyandian URL HTML , berisi daftar penyandian untuk karakter yang tidak valid.

CraigTP
sumber
Dan untuk karakter Unicode , artikel Wikipedia Persen-encoding mengatakan sebagai berikut: "Sintaksis URI generik mengamanatkan bahwa skema URI baru yang menyediakan untuk representasi data karakter dalam URI harus, pada dasarnya, mewakili karakter dari set tanpa pagu tanpa terjemahan, dan harus mengkonversi semua karakter lain menjadi byte sesuai dengan UTF-8, dan kemudian persen-encode nilai-nilai itu . "
DavidRR
9

Beberapa rentang karakter Unicode adalah HTML5 yang valid , meskipun mungkin masih bukan ide yang baik untuk menggunakannya.

Misalnya, hrefdokumen mengatakan http://www.w3.org/TR/html5/links.html#attr-hyperlink-href :

Atribut href pada elemen dan area harus memiliki nilai yang merupakan URL valid yang berpotensi dikelilingi oleh spasi.

Kemudian definisi "URL yang valid" menunjuk ke http://url.spec.whatwg.org/ , yang mengatakan bertujuan untuk:

Sejajarkan RFC 3986 dan RFC 3987 dengan implementasi kontemporer dan usang dalam proses.

Dokumen itu mendefinisikan poin kode URL sebagai:

Alfanumerik ASCII, "!", "$", "&", "'", "(", ")", "*", "+", ",", "-", ",". "," / " , ":", ";", "=", "?", "@", "_", "~", dan titik kode dalam rentang U + 00A0 hingga U + D7FF, U + E000 hingga U + FDCF , U + FDF0 ke U + FFFD, U + 10000 ke U + 1FFFD, U + 20000 ke U + 2FFFD, U + 30000 ke U + 3FFFD, U + 40000 ke U + 4FFFD, U + 50000 ke U + 5FFFD, U +60000 ke U + 6FFFD, U + 70000 ke U + 7FFFD, U + 80000 ke U + 8FFFD, U + 90000 ke U + 9FFFD, U + A0000 ke U + AFFFD, U + B0000 ke U + BFFFD, U + C0000 ke U + CFFFD, U + D0000 ke U + DFFFD, U + E1000 ke U + EFFFD, U + F0000 ke U + FFFFD, U + 100000 ke U + 10FFFD.

Istilah "poin kode URL" kemudian digunakan dalam pernyataan:

Jika c bukan titik kode URL dan bukan "%", kesalahan parse.

di beberapa bagian algoritma penguraian, termasuk skema, otoritas, jalur relatif, kueri, dan status fragmen: jadi pada dasarnya seluruh URL.

Juga, validator http://validator.w3.org/ meneruskan untuk URL seperti "你好", dan tidak lulus untuk URL dengan karakter seperti spasi"a b"

Tentu saja, seperti yang disebutkan oleh Stephen C, ini bukan hanya tentang karakter tetapi juga tentang konteks: Anda harus memahami keseluruhan algoritma. Tetapi karena kelas "titik kode URL" digunakan pada titik-titik kunci dari algoritma, itu yang memberikan ide bagus tentang apa yang dapat Anda gunakan atau tidak.

Lihat juga: Karakter Unicode dalam URL

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

Saya perlu memilih karakter untuk memisahkan url dalam string, jadi saya memutuskan untuk membuat daftar karakter yang tidak dapat ditemukan di URL sendiri:

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

Jadi, pilihan yang mungkin adalah baris baru, tab, spasi, garis miring terbalik dan "<>{}^|. Saya kira saya akan pergi dengan ruang atau baris baru. :)

Bunyk
sumber
2

Bukan jawaban untuk pertanyaan Anda, tetapi memvalidasi url benar-benar pita yang serius. Anda mungkin lebih baik memvalidasi nama domain dan meninggalkan bagian permintaan dari url. Itulah pengalaman saya. Anda juga dapat menggunakan ping url dan melihat apakah itu menghasilkan respons yang valid tetapi itu mungkin terlalu banyak untuk tugas yang begitu sederhana.

Ekspresi reguler untuk mendeteksi url berlimpah, google it :)

ChrisR
sumber
Jawaban ini menyarankan bahwa validasi URL adalah pekerjaan bukan untuk regex, tetapi untuk perpustakaan khusus bahasa / platform .
DavidRR
0

Saya menerapkan permintaan dan respons pembaca / penulis http (0.9, 1.0, 1.1) yang lama. Meminta URI adalah tempat yang paling bermasalah.

Anda tidak bisa hanya menggunakan RFC 1738, 2396 atau 3986 apa adanya. Ada banyak klien dan server HTTP lama yang memungkinkan lebih banyak karakter. Jadi saya sudah membuat penelitian berdasarkan sengaja diterbitkan akses webserver log: "GET URI HTTP/1.0" 200.

Saya menemukan bahwa karakter non-standar berikut sering digunakan di URI:

\ { } < > | ` ^ "

Karakter-karakter ini dijelaskan dalam RFC 1738 sebagai tidak aman .

Jika Anda ingin kompatibel dengan semua klien dan server HTTP lama - Anda harus mengizinkan karakter ini dalam URI permintaan.

Silakan baca informasi lebih lanjut tentang penelitian ini di http-og .

puchu
sumber
-4

Saya datang dengan beberapa ekspresi reguler untuk PHP yang akan mengonversi url dalam teks menjadi tag jangkar. (Pertama itu mengonversi semua url www ke http: // lalu mengonversi semua url dengan https?: // ke href = ... tautan 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
sumber
4
-1; Selain fakta bahwa keduanya melibatkan URL dalam kapasitas tertentu, ini tidak ada hubungannya dengan pertanyaan yang diajukan.
Mark Amery