NGINX: OCSP Stapling

25 Jan 2020

best-practices  http  nginx  ocsp  ocsp-stapling 

Share on:

Protokół OCSP pozwala na weryfikację stanu certyfikatu x509 i został zdefiniowany w RFC 6960 [IETF]. Zasada działania jest bardzo prosta: klient OCSP wysyła żądanie o stanie certyfikatu do respondera OCSP, a ten odsyła w odpowiedzi komunikaty (podpisane cyfrowo) w tym aktualny status, np. czy certyfikat jest nadal ważny lub został unieważniony.

Podgląd pochodzi ze świetnego artykułu High-reliability OCSP stapling and why it matters

W odpowiedzi na żądanie klienta mogą zostać zwrócone trzy komunikaty zwrotne:

Każdy CA (ang. Certificate Authority) posiada serwer do obsługi komunikatów OCSP, dzięki czemu może przesłać klientom cyfrowo podpisane komunikaty zawierające m.in. status certyfikatu.

Klient przesyłając zapytanie o certyfikat, wstrzymuje automatycznie akceptację takiego certyfikatu do momentu aż responder udzieli odpowiedzi.

I tutaj pojawia się pewna ważna kwestia: opóźnienia spowodowane oczekiwaniem na odpowiedź z serwera. Można zadać sobie również pytanie, co się stanie w przypadku niedostępności respondera? Rozwiązaniem tych problemów jest właśnie wykorzystanie mechanizmu OCSP Stapling.

OCSP Stapling #

W przeciwieństwie do „czystego” OCSP w mechanizmie OCSP Stapling przeglądarka użytkownika nie kontaktuje się z wystawcą certyfikatu, ale robi to w regularnych odstępach czasu przez serwer aplikacji.

OCSP Stapling definiuje komunikaty dotyczące żądania statusu certyfikatu w RFC 6066 - Certificate Status Request [IETF].

Korzystanie z OCSP bez implementacji rozszerzenia OCSP Stapling wiąże się ze zwiększonym ryzykiem utraty prywatności użytkownika, a także zwiększonym ryzykiem negatywnego wpływu na dostępność aplikacji z powodu braku weryfikacji ważności certyfikatu.

OCSP Stapling ma kilka zalet, w tym:

Ogólnie rzecz biorąc, rozszerzenie OCSP Stapling jest wykorzystywane w celu uzyskania lepszej wydajności, ponieważ serwer wysyła buforowaną odpowiedź OCSP tylko na żądanie klienta jako część rozszerzenia TLS, dlatego klient nie musi jej sprawdzać w adresie URL OCSP. Włączenie mechanizmu OCSP Stapling pozwala przenieść drugie żądanie sieciowe z przeglądarki internetowej na serwer. W przeciwieństwie do „czystego” OCSP w mechanizmie OCSP Stapling przeglądarka użytkownika nie kontaktuje się z wystawcą certyfikatu, ale robi to w regularnych odstępach czasu przez serwer aplikacji.

Dzięki takiemu rozwiązaniu będzie on okresowo komunikował się z urzędem certyfikacji, odbierając odpowiedź OCSP, a następnie odsyłając je, gdy przeglądarka internetowa rozpocznie połączenie za pomocą protokołu HTTPS. Dlaczego jest to istotne? W przypadku urządzeń mobilnych i sieci komórkowych sprawdzanie, czy certyfikat został odwołany, może spowodować wzrost narzutu połączenia nawet o 30% (patrz: Rethinking SSL for Mobile Apps), a niektórych sytuacjach jeszcze więcej.

Niestety, ta kontrola nie jest wykonywana równolegle. W większości przeglądarek do czasu zakończenia sprawdzania unieważnienia przeglądarka nie rozpocznie pobierania żadnych dodatkowych treści. Innymi słowy, sprawdzenie OCSP blokuje dostarczanie treści i nieodłącznie wydłuża żądanie o znaczną ilość czasu. Widzimy, że zaimplementowanie mechanizmu OCSP Stapling pozwala zaoszczędzić czas sprawdzania odwołania klienta i ma na celu zmniejszenie kosztu weryfikacji OCSP, poprawę wydajność komunikacji przeglądarki z serwerem aplikacji oraz pozwala na uzyskanie informacji o ważności certyfikatu w momencie uzyskiwania dostępu do aplikacji. Co równie ważne, prywatności użytkownika jest nadal zachowana. Więcej o wydajności tego rozwiązania poczytasz w artykule The impact of SSL certificate revocation on web performance.

Mechanizm OCSP Stapling to głównie optymalizacja, która nie ma możliwości zepsucia czegokolwiek, jeśli nie działa.

OCSP Stapling i NGINX #

Obsługa OCSP Stapling po stronie serwera NGINX nie jest skomplikowana, jednak należy pamiętać o kilku parametrach. W pierwszej kolejności, aby włączyć ten mechanizm, a także weryfikację po stronie serwera należy dodać następujące dyrektywy:

ssl_stapling on;
ssl_stapling_verify on;

Do poprawnego działania weryfikacji OCSP należy wskazać zaufane certyfikaty. NGINX generuje listę zaufanych certyfikatów z pliku certyfikatów wskazanego przez ssl_trusted_certificate. Co więcej, lista ta jest wymagana, aby sam mechanizm działał poprawnie, ponieważ certyfikat podmiotu podpisującego/wystawiającego certyfikat serwera powinien być znany.

Należy wysłać tę listę lub wyłączyć ssl_verify_client. Ten krok jednak jest opcjonalny, gdy pełny łańcuch certyfikatów, tj. tylko certyfikaty pośrednie, bez głównego urzędu certyfikacji, a także bez certyfikatu witryny, został już dostarczony z instrukcją ssl_certificate.

# Wskaż plik zaufanego certyfikatu CA (podmiotu, który podpisał CSR);
# certyfikaty pośrednie tylko, jeśli NGINX nie może znaleźć certyfikatów
# najwyższego poziomu z certyfikatu ssl_certificate:
ssl_trusted_certificate /etc/nginx/ssl/inter-CA-chain.pem

W przypadku, gdy używany jest tylko certyfikat serwera (bez części urzędu certyfikacji), potrzebna jest właśnie dyrektywa ssl_trusted_certificate w celu wskazania pełnego łańcucha. Myślę, że najbezpieczniejszym sposobem jest uwzględnienie wszystkich odpowiednich certyfikatów, tj. głównego i pośredniego urzędu certyfikacji. Co więcej, lista certyfikatów wskazanych za pomocą dyrektywy ssl_trusted_certificate nie zostanie wysłana do klienta.

Oficjalna dokumentacja wyjaśnia to w poniższy sposób:

For the OCSP stapling to work, the certificate of the server certificate issuer should be known. If the ssl_certificate file does not contain intermediate certificates, the certificate of the server certificate issuer should be present in the ssl_trusted_certificate file.

Oba typy łańcuchów (RootCA + certyfikaty pośrednie lub tylko certyfikaty pośrednie) będą działać jako ssl_trusted_certificate do celów weryfikacji OCSP. Certyfikat CA nie jest zalecany i nie jest potrzebny w ssl_certificate.

Jeśli używasz Let’s Encrypt, nie musisz dodawać RootCA (do ssl_trusted_certificate), ponieważ odpowiedź OCSP jest podpisana przez sam certyfikat pośredni.

Kolejny parametr odpowiada za rozwiązywanie nazwy hosta respondera OCSP i jest on opcjonalny. Ja jednak zawsze używam najbardziej stabilnego i najmniej podatnego na awarię serwera rozpoznawania nazw, takiego jak Google 8.8.8.8, Quad9 9.9.9.9, CloudFlare 1.1.1.1 lub OpenDNS 208.67.222.222 (najprawdopodobniej najczęstszym sposobem rozwiązywania domen wewnętrznie i zewnętrznie będzie wykorzystanie Bind9 lub czegokolwiek innego).

Jeśli dyrektywa resolver nie zostanie dodana lub serwer NGINX nie będzie miał dostępu na do sieci publicznej, resolver domyślnie przyjmuje domyślną wartość DNS serwera.

# Aby rozwiązać nazwę hosta respondera OCSP, ustaw resolvery i czas ich buforowania:
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

Spójrz, co mówi na ten temat oficjalna dokumentacja:

To prevent DNS spoofing (resolver), it is recommended configuring DNS servers in a properly secured trusted local network.

Powinieneś wiedzieć, że zbyt krótki limit czasu resolvera (domyślnie 30 sekund) może być powodem niepowodzenia wykonania mechanizmu OCSP Stapling. Jeśli dyrektywa resolver_timeout jest ustawiona na bardzo niską wartość (<5 sekund), mogą pojawić się komunikaty w pliku dziennika takie jak: “[…] ssl_stapling” ignored, host not found in OCSP responder […].

W przypadku serwera NGINX należy również pamiętać o tzw. powolnym ładowaniu odpowiedzi OCSP (ang. lazy-loads OCSP responses). Pierwsze żądanie nie będzie miało zszywanej odpowiedzi (ang. stapled response) , ale kolejne już tak. Wynika to z faktu, że NGINX nie pobiera wstępnie odpowiedzi OCSP podczas uruchamiania serwera (lub po ponownym załadowaniu).

Na koniec dwie komendy, za pomocą których można przetestować działanie OCSP Stapling:

# 1)
openssl s_client -connect example.com:443 -servername example.com -tlsextdebug -status

# 2)
echo | openssl s_client -connect example.com:443 -servername example.com -status 2> /dev/null | grep -A 17 'OCSP'

Przydatne zasoby: