OpenSSL: mechanizm SNI oraz rozszerzenie SAN

11 Sep 2015

openssl  san  security  sni  testing  tls 

Share on:

Za pomocą biblioteki openssl można przetestować praktycznie każdą usługę opartą na protokołach SSL/TLS. Po nawiązaniu połączenia można nimi sterować, stosując komendy/wiadomości specyficzne dla każdego protokołu warstwy aplikacji.

W tym poście przedstawię, czym jest mechanizm SNI a także do czego potrzebne jest rozszerzenie SAN. Dodatkowo zaprezentuję, w jaki sposób nawiązywać połączenia z wykorzystaniem tego pierwszego.

Czym jest rozszerzenie SNI? #

SNI (ang. Server Name Indication) jest rozszerzeniem protokołu TLS, które umożliwia klientowi (na przykład przeglądarce) na podanie dokładnej nazwy hosta, na początku procesu uzgadniania TLS, z którym klient próbuje się połączyć się (wskazuje, z którą nazwą hosta kontaktuje się przeglądarka na początku procesu uzgadniania). Po stronie serwera HTTP pozwala na wielokrotne połączenie przy użyciu tego samego adresu IP i numeru portu, bez konieczności używania wielu adresów IP, dzięki czemu możliwe jest używanie wielu certyfikatów na jednym adresie IP.

Gdy przeglądarka zestawia połączenie z serwerem, nie wie on, o jaką stronę zostanie poproszony, jednak już w tym momencie musi przedstawić poprawny certyfikat dla domeny. Jak wiesz, na serwerze może znajdować się wiele domen, które mają wspólny adres IP, a każda witryna ma własny certyfikat. W takiej sytuacji serwer może nie wiedzieć, który z nich ma zostać zwrócony klientowi, gdy próbuje on bezpiecznie połączyć się z jedną domen. Dzieje się tak, ponieważ uzgadnianie SSL/TLS następuje, zanim klient wskaże przez HTTP, z którą witryną się łączy.

Klient dodaje rozszerzenie SNI zawierające nazwę hosta witryny, z którą się łączy, do komunikatu ClientHello. Niestety ta wiadomość jest wysyłana w postaci niezaszyfrowanej, ponieważ klient i serwer nie mają w tym momencie wspólnego klucza szyfrowania. Wada tego jest oczywista: szpiegujący, który znajduje się na ścieżce komunikacji, może przechwycić wiadomość ClientHello i określić, z którą witryną klient próbuje się połączyć. Dzięki temu może śledzić, które witryny odwiedza użytkownik.

Rozwiązaniem tego problemu jest użycie funkcji ESNI, która zapewnia prywatność danych przeglądania użytkownika, poprzez szyfrowanie części wskazującej nazwę serwera (SNI) w uzgadnianiu TLS. Więcej informacji na ten temat znajdziesz w pracy On the Importance of Encrypted-SNI (ESNI) to Censorship Circumvention.

Zobacz poniższy diagram z przykładem komunikacji z rozszerzeniem SNI:

Ponieważ liczba dostępnych adresów IPv4 stale maleje, pozostałe mogą być alokowane bardziej efektywnie. W większości przypadków można uruchomić aplikację z obsługą protokołu SSL/TLS bez konieczności zakupu dodatkowego adresu IP.

Zgodnie z RFC 6066 - Server Name Indication [IETF] rozszerzenie to pozwala klientowi na wskazanie nazwy hosta, z którym stara się nawiązać połączenie na początku procesu uzgadniania sesji SSL/TLS. Jak zostało powiedziane wyżej — pozwala to serwerowi na przedstawienie wielu certyfikatów na tym samym gnieździe (adresie IP i numerze portu), dzięki czemu możliwe jest korzystanie z tego samego adresu IP przez wiele witryn wykorzystujących protokół HTTPS.

Żądana nazwa hosta (domeny), którą ustala klient podczas połączenia, nie jest szyfrowana. W związku z tym, podsłuchując ruch, można zobaczyć, z którą witryną nawiązywane będzie połączenie.

Proces nawiązywania połączenia #

Podczas nawiązywania połączenia TLS klient wysyła żądanie z prośbą o certyfikat serwera. Gdy serwer odsyła certyfikat do klienta, ten sprawdza go i porównuje nazwę z nazwami zawartymi w certyfikacie (pola CN oraz SAN). Jeżeli domena zostanie znaleziona, połączenie odbywa się w normalny sposób (standardowa sesja SSL/TLS). Jeżeli domena nie zostanie znaleziona, oprogramowanie klienta powinno wyświetlić ostrzeżenie, zaś połączenie powinno zostać przerwane.

Niedopasowanie nazw może oznaczać próbę ataku typu MitM. Niektóre z aplikacji (np. przeglądarki internetowe) pozwalają na ominięcie ostrzeżenia w celu kontynuowania połączenia — przerzucając tym samym odpowiedzialność na użytkownika, który często jest nieświadomy czyhających zagrożeń.

Warto wiedzieć, że przeglądarka Chrome (od wersji 58), podobnie jak większość przeglądarek internetowych, całkowicie ignoruje pole CommonName i wymaga poprawnych domen w rozszerzeniu subjectAltName, które to używane jest do dopasowania nazwy domeny i certyfikatu witryny. Jeżeli pominiemy rozszerzenie i ustawimy domeny w polu CN, zostanie wyświetlony komunikat NET::ERR_CERT_COMMON_NAME_INVALID, który moim zdaniem jest mylący i nieprawidłowy. Prawdziwa przyczyna błędu jest tak naprawdę inna: nazwa odwiedzanej witryny nie jest uwzględniona w certyfikacie.

Szczegóły połączenia #

Gdy klient (np. przeglądarka) nawiązuje połączenie, ustawia specjalny nagłówek HTTP (nagłówek Host) określający, do której witryny klient próbuje uzyskać dostęp. Serwer dopasowuje podaną zawartość nagłówka do domeny w swojej konfiguracji i odpowiada klientowi np. wyświetlając odpowiednią zawartość lub kierując ruch dalej i w konsekwencji także serwując odpowiednią treść.

Podanej techniki nie można zastosować do protokołu HTTPS, ponieważ nagłówek ten jest wysyłany dopiero po zakończeniu uzgadniania sesji TLS. Tym samym powstaje następujący problem:

Dlatego do tej pory (przed wprowadzeniem rozszerzenia SNI) jedynym sposobem dostarczania różnych certyfikatów było hostowanie jednej domeny na jednym adresie IP. Na podstawie adresu IP (do którego doszło żądanie o zaserwowanie treści) oraz przypisanej do niego domeny serwer wybierał odpowiedni certyfikat.

Pierwszym rozwiązaniem tego problemu w przypadku ruchu HTTPS jest przejście na protokół IPv6.

Nie stanowi to oczywiście problemu w przypadku protokołu HTTP, ponieważ jak tylko połączenie TCP zostanie otwarte, klient wskaże, do której strony internetowej próbuje dotrzeć w żądaniu.

Rozwiązaniem tymczasowym jest właśnie wykorzystanie mechanizmu SNI, który wstawia żądaną nazwę hosta (domeny, adresu internetowego) w ramach uzgadniania ruchu TLS — przeglądarka wysyła tę nazwę w komunikacie ClientHello pozwalając serwerowi na określenie najbardziej odpowiedniego certyfikatu.

SNI dodaje nazwę domeny do procesu uzgadniania TLS, dzięki czemu klient dotrze do właściwej domeny i otrzyma prawidłowy certyfikat SSL, tym samym będzie możliwe normalne kontynuowanie sesji TLS oraz przejście poziom wyżej, do wymiany danych na poziomie protokołu HTTP z pełnym i bezpiecznym wykorzystaniem TLS.

Spójrz na poniższy obrazek:

Dzięki rozszerzeniu SNI serwer może bezpiecznie „trzymać” wiele certyfikatów używając pojedynczego adresu IP.

SNI a SAN #

SAN i SNI to dwie całkowicie różne rzeczy, których jedyną cechą wspólną jest to, że wymagają obsługi po stronie klienta.

SAN (ang. Subject Alternative Name) jest częścią specyfikacji X509, w której certyfikat zawiera pole z listą nazw alternatywnych (np. dodatkowych domen). Są one tak samo ważne jak standardowe pole CN, które notabene jest ignorowane przez większość klientów, jeżeli wskazano pole SAN.

Rozszerzenie to wykorzystuje się głównie, aby rozwiązać problem stosowania jednego certyfikatu dla jednej domeny, co jest oczywiście bardzo niepraktyczne i zwiększa znacząco koszty utrzymania. Dzięki temu serwer nie musi przedstawiać innego certyfikatu dla każdej domeny, tylko za pomocą jednego certyfikatu, w którym zawarte jest jedno pole CN (dla domeny głównej) oraz rozszerzenie SAN (w którym określamy domenę główną oraz dodatkowe domeny) umożliwia obsługę wielu domen w jednym certyfikacie.

Oto przykład:

issuer: DigiCert SHA2 Secure Server CA (DigiCert Inc)
owner: Lucas Garron Torres
cn: *.badssl.com  >>> POLE CN
san: *.badssl.com badssl.com  >>> POLE SAN
sni: not match
validity: match
 └─0:*.badssl.com 34383cd7 ★
   ├   DigiCert SHA2 Secure Server CA 85cf5865
   └─1:DigiCert SHA2 Secure Server CA 85cf5865 ✓
     └ DigiCert Global Root CA 3513523f
verification: ok

SNI a klient #

Jak już powiedziałem na wstępie, SNI to rozszerzenie protokołu TLS, które jest swego rodzaju odpowiednikiem nagłówka Host protokołu HTTP. Pozwala serwerowi wybrać odpowiedni certyfikat, który ma zostać przedstawiony klientowi, bez ograniczenia korzystania z oddzielnych adresów IP po stronie serwera (upraszcza to posiadanie wielu certyfikatów).

Poprawne działanie tego rozszerzenia zależy od:

Zdecydowana większość przeglądarek i systemów operacyjnych obsługuje SNI. W przypadku nieaktualnych klientów, którzy nie wspierają tego rozszerzenia, użytkownik prawdopodobnie nie będzie mógł uzyskać dostępu do niektórych witryn, a przeglądarka zwróci komunikat „Połączenie nie jest prywatne”.

Testowanie połączenia #

OpenSSL #

Połączenie do zdalnej usługi z ustaloną nazwą domeny (rozszerzenie SNI):

echo | openssl s_client -showcerts -servername www.example.com -connect example.com:443

Jeżeli chcemy połączyć się bez włączonego SNI:

echo | openssl s_client -showcerts -connect example.com:443

gnutls-cli #

Wykorzystujemy rozszerzenie SNI (domyślnie):

gnutls-cli -p 443 www.example.com

Bez wykorzystania rozszerzenia SNI:

gnutls-cli --disable-sni -p 443 www.example.com

Dodatkowe zasoby #