Každá část URL a co smí obsahovat

Popis všech částí URL na konkrétním příkladu, které znaky se kde musí procentově zakódovat, jak se dotazovací řetězce doopravdy chovají a jak poznat odkaz, který lže o svém hostu.

URL je jeden řetězec, který říká, jakým protokolem se má mluvit, s jakým strojem a na co se má zeptat. Přečíst ji správně je hlavně otázka toho, vědět, kde která část končí, protože oddělovači jsou jednotlivé znaky a rozhoduje ten první.

https://alex:s3cret@www.example.co.uk:8443/docs/intro?lang=en&page=2#notes
│       │           │                 │   │           │              └─ fragment
│       │           │                 │   │           └──────────────── query
│       │           │                 │   └──────────────────────────── path
│       │           │                 └──────────────────────────────── port
│       │           └────────────────────────────────────────────────── host
│       └────────────────────────────────────────────────────────────── userinfo
└────────────────────────────────────────────────────────────────────── scheme

Jednotlivé části

ČástOhraničenaK čemu sloužíDorazí na server
Schémakončí u prvního :Jaký protokol a jaká pravidla platí pro zbytekUrčuje spojení
Userinfoza //, končí u @Přihlašovací údaje, u http a https dávno zastaraléJen pokud se je klient rozhodne odeslat
Hostkončí u :, /, ? nebo #Doménové jméno nebo IP adresa, kterou je třeba přeložit a připojit se k níAno, v hlavičce Host
Portza :, končí u /, ? nebo #TCP port, výchozí 80 pro http a 443 pro httpsPoužije se pro spojení
Cestazačíná u /, končí u ? nebo #Který zdroj na daném hostuAno, v řádku požadavku
Dotazzačíná u ?, končí u #Parametry pro daný zdrojAno, v řádku požadavku
Fragmentzačíná u #, jde až do konceMísto uvnitř zdrojeNe

Host a port dohromady tvoří autoritu. Fragment je výjimka: odstraní se ještě před sestavením požadavku, takže ho server nikdy neuvidí. Prohlížeče ho používají pro kotvy a směrování na straně klienta a některé autentizační postupy záměrně vracejí tokeny ve fragmentu, aby se tyto tokeny nikdy neobjevily v logu serveru. Stejně ale skončí v historii prohlížeče, takže není soukromý, jen se neodesílá.

Vyhrazené a nevyhrazené znaky

Procentové kódování zapisuje bajt jako % a dvě hexadecimální číslice, přičemž vychází z UTF-8 bajtů daného znaku. Z é se stane %C3%A9, dva bajty a dvě escape sekvence.

Vedle písmen a číslic jsou nevyhrazené čtyři znaky, které se nikde kódovat nemusí: - . _ ~. Všechno ostatní je buď vyhrazené, což znamená, že to má někde v URL strukturní úlohu, nebo se to musí zakódovat. Vyhrazená množina je : / ? # [ ] @ a ! $ & ' ( ) * + , ; =.

Slovo „vyhrazený“ neznamená „vždy zakázaný“. Znamená, že znak je v některé části oddělovačem a kódovat se musí jen v těch částech, kde by se jako oddělovač četl.

ZnakV segmentu cestyV hodnotě dotazu
Mezera%20, vždy%20 nebo +
/%2F, jinak rozdělí segmentLegální tak, jak je
?%3F, jinak tam cesta končíLegální tak, jak je
#%23, vždy%23, vždy
& a =Legální tak, jak jsou%26 a %3D, jinak se dvojice rozpadne
+Legální a znamená plus%2B, jinak se může dekódovat jako mezera

To je to praktické pravidlo: zakódujte znak, který by ukončil část, ve které se právě nacházíte. Mezera v cestě musí být %20, protože nezakódovaná mezera ve většině parserů URL ukončí, zatímco mezera v hodnotě dotazu může být %20 nebo starší +. Spolehlivý způsob, jak to udělat, je URL Encoder, protože zakódovat celou URL a zakódovat jednu její komponentu jsou dvě různé operace a téměř vždy je potřeba ta druhá.

Dotazovací řetězce jsou konvence

Specifikace říká jen to, že dotaz je všechno mezi ? a #, složené z povolených znaků. Tvar key=value&key=value pochází z kódování HTML formulářů, ne ze standardu URI. Nic nebrání serveru, aby si ?a:1;b:2 parsoval, jak se mu zlíbí.

Protože je tento tvar jen konvence, opakované klíče nemají definovaný význam a každý ekosystém si zvolil vlastní odpověď:

Chování pro ?id=1&id=2Kde
Obě hodnoty jako seznamPython parse_qs, Node querystring, Express
Jen první hodnotaGo Query().Get, Java getParameter
Jen poslední hodnotaPHP, Rails
Spojené do 1,2Kolekce požadavků v ASP.NET

Pole dědí stejný problém. ids=1&ids=2, ids[]=1&ids[]=2, ids[0]=1&ids[1]=2 i ids=1,2 se všechny běžně používají a jen server rozhoduje, kterému z nich rozumí. Zápis s hranatými závorkami se musí zakódovat, aby byl přísně vzato legální (ids%5B%5D=1), i když většina serverů nezakódované závorky přijme. Vyberte tvar, který dokumentuje přijímající strana, a pak ho dodržujte.

Znaménko plus si zaslouží vlastní varování. V datech application/x-www-form-urlencoded, tedy v těle požadavku zakódovaném jako formulář, znamená + mezeru. V cestě URL znamená doslovné plus. V dotazovacím řetězci to závisí na parseru a většina webových frameworků tam formulářové dekódování aplikuje, takže se z + tiše stane mezera. Každá hodnota, která legitimně může obsahovat plus, například standardní výstup Base64 nebo telefonní číslo, se musí posílat jako %2B.

Relativní odkazy

Relativní odkaz se vyhodnotí vůči základní URL tak, že se nahradí všechno za posledním / v základní URL. To poslední lomítko je celé pravidlo a právě proto na koncovém lomítku záleží.

ZákladOdkazVýsledek
https://ex.com/docs/introguidehttps://ex.com/docs/guide
https://ex.com/docs/intro/guidehttps://ex.com/docs/intro/guide
https://ex.com/docs/intro/guidehttps://ex.com/guide
https://ex.com/docs/intro/../guidehttps://ex.com/docs/guide
https://ex.com/docs/intro?a=1?b=2https://ex.com/docs/intro?b=2
https://ex.com/docs/intro?a=1#tophttps://ex.com/docs/intro?a=1#top
https://ex.com/docs/intro//cdn.ex.com/x.jshttps://cdn.ex.com/x.js

Poslední řádek je odkaz relativní vůči schématu: dvě úvodní lomítka zachovají schéma základní URL a nahradí autoritu. Odkaz, který začíná na ?, zachová cestu a zahodí původní dotaz, a odkaz, který začíná na #, zachová obojí.

Kdy jsou dvě URL týž zdroj

Schéma a host nerozlišují velikost písmen, takže HTTPS://Example.COM a https://example.com jsou jedna adresa. Všechno za hostem velikost písmen podle standardu rozlišuje, i když se konkrétní server může rozhodnout ji ignorovat.

Tyto dvojice jsou ekvivalentní:

  • https://example.com:443/a a https://example.com/a, protože port je výchozí
  • https://example.com a https://example.com/, protože prázdná cesta znamená kořen
  • /a/%7Euser a /a/~user, protože ~ je nevyhrazený a jeho procentové zakódování nic nemění

Tyto dvojice ekvivalentní nejsou:

  • /docs a /docs/, což jsou různé zdroje, i když většina serverů jeden na druhý přesměruje
  • /p?a=1&b=2 a /p?b=2&a=1, protože pořadí parametrů je součástí řetězce
  • /index.html a /, pokud server neurčí jinak

Cache a CDN klíčují podle přesných bajtů, takže přidaný sledovací parametr nebo přeházený dotaz rozdělí jeden uložený objekt na dva a sníží úspěšnost cache na polovinu. Vyhledávače považují varianty za duplicitní stránky, dokud kanonický odkaz neukáže na jeden zvolený tvar. Řešením je vybrat jediný tvar, zbytek na něj přesměrovat pomocí 301 a odstranit parametry, které odpověď nemění.

Délka, logy a soukromí

Specifikace žádný limit délky nestanoví, ale limity existují všude jinde. Běžné výchozí konfigurace serverů omezují celý řádek požadavku zhruba na 8 KB, některé proxy a hardwarová zařízení na 4 KB a starší klienti asi na 2 KB. Cokoli, co musí přežít e-mailové klienty, QR kódy a zkracovače odkazů, je nejbezpečnější pod zhruba 2000 znaky.

Silnější argument pro krátké URL je ten, že dotazovací řetězec není soukromý. Zapisuje se do přístupových logů, předává se třetím stranám v hlavičce Referer, zůstává v historii prohlížeče, ukládá se se záložkami a kopíruje se pokaždé, když někdo odkaz sdílí. Relační tokeny, kódy pro reset hesla, API klíče a osobní údaje tam proto nepatří. Data, která jsou objemná nebo citlivá, patří do těla požadavku, které nemá praktický strop velikosti a ve výchozím nastavení se neloguje.

Jak si přečíst odkaz, než na něj kliknete

Autorita začíná za :// a končí u úplně prvního /, ? nebo #. Přečtěte si tento úsek a pak jeho poslední dvě nebo tři části. To je skutečný host a nic nalevo od něj na tom nic nemění.

https://www.paypal.com@198.51.100.7/secure/login
        ^^^^^^^^^^^^^^ ^^^^^^^^^^^^
        userinfo       real host

Běžné maskování, které je dobré poznat:

  • Text před @ je userinfo, nikdy ne host, a může to být naprosto libovolné jméno značky.
  • paypal.com.secure-login.example je subdoménou domény secure-login.example. Části domény se čtou zprava doleva.
  • https://short.example/https://www.bank.com/login vkládá celou přesvědčivou URL do cesty.
  • Host začínající na xn-- je Punycode, tedy ASCII podoba internacionalizovaného doménového jména vytvořená pomocí IDNA. münchen.de je na drátě xn--mnchen-3ya.de, což je legitimní, ale stejný mechanismus umožňuje homografy: cyrilské а (U+0430) je vizuálně totožné s latinským a a doména, která ho použije, se zakóduje na něco jako xn--pple-43d.com. Prohlížeče zobrazují podobu v Punycode, když se v jedné části domény mísí různá písma, jenže pravidla se liší a nejsou zárukou.
  • Zakódované oddělovače jako %2F nebo %40 uvnitř hostu jsou záměrným pokusem zmást parser.

Vložení odkazu do nástroje URL Parser to vyřeší v jednom kroku, protože skutečný parser aplikuje stejné pravidlo „vyhrává první oddělovač“ jako prohlížeč a host zobrazí samostatně. Při kontrole mnoha odkazů najednou je Regex Tester rychlý způsob, jak ověřit, které z nich skutečně mají autoritu, kterou očekáváte.