IP adresa na každý web zvlášť
Poměrně často se na fórech objevuje dotaz, jak si na své stránky na sdíleném hostingu přidat podporu SSL. Tazatel je velmi rychle zklamán, protože se dozví, že to vůbec není možné. Pro šifrované spojení s web serverem totiž potřebujete pro každou doménu vlastní IP adresu. To standardně hostingy nenabízejí, někdy je možné si za adresu připlatit.
Potíž je, že IPv4 adres je málo a IPv6 není dostatečně rozšířené. Webhosteři proto nemohou přidělovat hromady adres každému zákazníkovi, proto je běžné, že na jedné IP běží stovky různých domén a webů. Za normálních okolností to nikomu nevadí a je to v pořádku, potíž nastane právě ve chvíli, kdy chcete komunikaci se svým webem šifrovat pomocí SSL.
Stejný problém se dotýká i uživatelů všemožných VPS, které jsou dnes velmi populární. K běžné VPS dnes dostanete jednu IPv4 adresu a nějaký rozsah IPv6. A jste v podobné situaci: můžete provozovat jen jeden SSL web, protože máte jen jednu adresu.
Proč to tak je?
Důvod je poměrně prostý: hlavička „Host“, kterou si klient vybírá, se kterým virtuálním serverem bude komunikovat, je součástí protokolu HTTP 1.1. Komunikace jeho prostřednictvím však probíhá až uvnitř šifrovaného spojení. Když je toto spojení navazováno, server ještě netuší, o jaké spojení má klient zájem. Nemůže tedy vybírat z více SSL certifikátů.
Standardní spojení probíhá tak, že se klient připojí k IP adrese získané z DNS na port 80 a pošle svůj požadavek, který může vypadat například takto:
GET / HTTP/1.1 Host: www.root.cz
Součástí požadavku je nejen informace o souboru, který chceme poslat, ale i název virtuálního serveru, se kterým si přejeme komunikovat. Pro server je pak snadné takový požadavek vyřídit.
V případě HTTPS spojení se klient připojí na port 443 a rovnou posílá zprávu ClientHello. V ní serveru oznámí, že je připraven navázat SSL spojení a informuje jej, jaké verze protokolů umí. Server odešle v odpovědi svou zprávu ServerHello, ve které informuje o tomtéž. Poté server předá klientu certifikát, obě strany si dohodnou šifrovací klíč a kanál je spojen.
Teprve uvnitř této šifrované cesty proběhne samotný HTTP požadavek, ve kterém se server dozvídá, se kterým serverem se bude komunikovat. Protože nemá tuhle informaci na začátku, nemůže po svém ServerHello vybírat z více certifikátů a musí poslat jeden konkrétní. Ten ale obsahuje také údaje o doméně, majiteli a podobně. Je proto platný jen pro jednu doménu.
Vzniká tak problém Macha a Šebestové: „Bez brýlí se brýle špatně hledají.“ Neboli: server se neumí rozhodnout podle informace, kterou ještě nemá. Proto není možné na jedné IP adrese provozovat více HTTPS virtuálních serverů.
Poznámka k certifikátům: Ani vystavení certifikátu pro více domén není dobrým řešením, protože certifikát je vystaven na jednu osobu či organizaci. Všechny domény na společném serveru by proto musela vlastnit jedna organizace. Navíc při každé změně by bylo třeba certifikát předělávat.
Řešení se jmenuje SNI
Pokud jste se nad celým procesem alespoň trochu zamysleli, asi vás napadlo, jak by bylo možné problém řešit. Stačilo by informaci o serveru propašovat někam před předání certifikátů, aby server informaci dostal včas. Jediné místo, kam je možné informaci vložit, je zmíněná zpráva ClientHello. Vězte, že už to napadlo někoho před vámi (jako obvykle) a toto rozšíření se jmenuje SNI, neboli Server Name Indication.
Vzniklo už v roce 2004, ale trvalo tři roky, než se patch dostal do OpenSSL 0.9.8 a do dalších aplikací postupně proniká dodnes, jak si řekneme dále. Aby bylo možné SNI použít, musí jej podporovat klientská i serverová strana. Klient musí správně odeslat informaci během svého ohlášení a server ji musí rozeznat a správně využít. Podpora v software je tedy klíčová.
Kdo to umí a kdo ne?
Nejprve si probereme podporu u serverů, kterou budeme mít rychle za sebou. SNI podporují následující web servery:
- Apache od verze 2.2.12 s modulem mod_ssl i mod_gnutls
- Lighttpd od verze 1.4.24
- Nginx, pokud má k dispozici OpenSSL se správnou podporou
- Apache Tomcat na Java 7
- Microsoft Internet Information Server IIS 8
- LiteSpeed od verze 4.1
- a některé další
U serverů tedy můžeme prohlásit, že podpora je bezproblémová. Složitější to ale budeme mít u klientů. Na desktopu můžeme říct, že podpora v aktuálních verzích prohlížečů a systémů k dispozici je. Pokud použijete Firefox alespoň verze 2.0, Operu 8.0, Chrome 6, Safari 2.1, či Konqueror v KDE 4.7, bude vám vše fungovat.
Musíme se ale zastavit u speciálního případu – Windows XP. Tento deset let starý systém samozřejmě podporu v integrované SSL knihovně nenabízí.
Dobrá zpráva je, že „externí“ prohlížeče si s sebou přinášejí i vlastní implementaci SSL knihovny, takže na té systémové nejsou závislé. Výjimkou je Internet Explorer, který pochopitelně maximálně využívá služby systému. V něm tedy SNI nefunguje, ale jen ve Windows XP. Ve vyšších verzích Windows (dnes Vista a 7) je SNI podporováno i v Internet Exploreru.
Na desktopu je tedy problémová jen kombinace Windows XP a Internet Explorer. Windows XP mají mezi uživateli pořád poměrně velké zastoupení, asi 38 %. Pokud ale provedeme průnik uživatelských skupin a zaměříme se jen na XP s Internet Explorerem, dojdeme k číslu 17 % (získáno ze služby Navrcholu.cz). To už není zdaleka tak vysoké. Musíme ale počítat s tím, že této skupině uživatelů nebude SNI fungovat (viz další text).
Ještě komplikovanější je to na mobilních platformách. Tam jsem to musel osobně vyzkoušet, podařilo se mi to na Androidu, iOS a Symbianu. Aktuální situace je následující:
- iOS zřejmě od verze 4.0 (na 3.1 nefungoval a na 4.1 a vyšších ano)
- Symbian SNI vůbec neumí
- Android nabízí plnou podporu až od verze 3.0, ale viz dále
Musím se ještě zdržet u Androidu. Podle informací, které se mi podařilo získat, je Android připraven i v nižších verzích, ale záleží na prohlížeči, zda podporu využije. Mám nejnovější mobilní verzi 2.3.7 a integrovaný prohlížeč s lakonickým názvem „Prohlížeč“ SNI neumí. Některé alternativní prohlížeče si s ním ale poradí. Zkoušel jsem tyto:
- Firefox ano
- Opera Mini neřeší
- Opera Mobile různě
- Dolphin ne
Zajímavě se chová Opera Mini, která se sice tváří, že na ní SNI funguje, ale realita je taková, že Mini vůbec validitu SSL certifikátu neřeší. Klidně si nechá podstrčit prošlý certifikát nebo certifikát pro úplně jinou doménu a neozve se. Pozor tedy na její použití.
Poznámka: Na LinuxAltu mi různí lidé předvedli různou podporu na mnoha telefonech. Na některých z nich SNI v Opeře Mobile fungovalo a jinde ne. Podpora je tedy zjevně platformně závislá a je závislá pravděpodobně i na dalších okolnostech (verze Androidu?). Bohužel nemám víc informací, můžete si to sami vyzkoušet a podělit se o zkušenosti v diskusi.
Pokud si chcete podporu na svém zařízení vyzkoušet, navštivte weby alice.sni.velox.ch a hlavně bob.sni.velox.ch. První jmenovaný by měl fungovat všude, pokud ale nemáte SNI, na Bobovi by na vás měl prohlížeč zakřičet, že dostal certifikát k jiné doméně (Alici).
Co když to můj prohlížeč neumí?
Jelikož podpora není stoprocentní, je Pro nasazení SNI v praxi klíčová otázka: „Co se stane, když na SNI web přijdu s prohlížečem bez podpory?“ Dobrá zpráva: nestane se nic kritického, uživatel se na web dostane. Jen dostane pravděpodobně špatný certifikát.
Jelikož se bez SNI server nedozví, který certifikát má předat, musí předat ten standardní. Obvykle je to ten, který je v konfiguraci uveden jako první. Tady je možné využít jistou taktiku a nasadit nejnavštěvovanější doménu jako první. Pokud totiž uživatel bez podpory SNI navštíví web zapsaný v konfiguraci jako první, dostane „náhodou“ správný certifikát a vše mu funguje.
Pokud ovšem navštíví jiný virtuální server, jeho prohlížeč bude nadávat, že dostal certifikát ke špatné doméně.
Je tedy potřeba při nasazení SNI zvážit, jací uživatelé budou na mé servery chodit a nastavit server tak, aby co nejméně z nich vidělo chybovou hlášku o neplatném certifikátu.
Nasazení v praxi
Na závěr teoretického článku si předvedeme praktické nasazení SNI na serveru. Jedná se o poměrně jednoduchou záležitost, kterou si ukážeme na serverech lighthttpd a Apache. Nejprve ke konfiguraci prvního jmenovaného. Konfigurace bude vypadat následovně:
$SERVER["socket"] == ":443" { ssl.engine = "enable" ssl.pemfile = "/etc/lighttpd/vychozicertifikat.pem" ssl.ca-file = "/etc/lighttpd/ca-chain.pem" } $HTTP["host"] == "www.domena1.cz" { server.document-root = "/var/www/domena1.cz" ssl.pemfile = "/etc/lighttpd/certifikat1.pem" } $HTTP["host"] == "www.domena2.cz" { server.document-root = "/var/www/domena2.cz" ssl.pemfile = "/etc/lighttpd/certifikat2.pem" }
Poznámka: Nezapomeňte, že Light očekává v souboru s certifikátem také privátní klíč. Je třeba je tedy (třeba pomocí cat
) spojit do jednoho souboru a ten mu pak ukázat v konfiguraci.
Podobně jednoduché to bude i u Apache:
# Poslouchej na správném portu a na všech IP Listen 443 NameVirtualHost *:443 # Přijímej i uživatele bez SNI SSLStrictSNIVHostCheck off <VirtualHost *:443> DocumentRoot /var/www/domena1.cz ServerName www.domena1.cz GnuTLSCertificateFile /etc/apache2/certifikat1.pem GnuTLSKeyFile /etc/apache2/klic1.key </VirtualHost> <VirtualHost *:443> DocumentRoot /var/www/domena2.cz ServerName www.domena2.cz GnuTLSCertificateFile /etc/apache2/certifikat2.pem GnuTLSKeyFile /etc/apache2/klic2.key </VirtualHost>
SNI je použitelné, má ale svá ale
SNI je poměrně dobře nasaditelná technika, která ulehčí život především uživatelům VPS s jednou IP adresou. Nevím bohužel o žádném webhostingu, který by SNI podporoval. Pokud o nějakém takovém víte, určitě jej doporučte v diskusi.
Překážkou v nasazení může být jedině nedokonalá podpora v prohlížečích. Na desktopu jsou omezeni jen uživatelé Windows XP s Internet Explorerem, větší problémy ale budou mít uživatelé mobilních telefonů. Je proto třeba před nasazením provést analýzu konkrétní situace na vašich webech.