Public Key Pinning realizowany poprzez nagłówek protokołu HTTP pozwala na poinformowanie klienta (przeglądarki) o powiązaniu danego klucza publicznego z konkretnym serwerem (domeną).
Z tego wpisu dowiecie się jak został zaprojektowany, jak działa, jakie przynosi korzyści i w jaki sposób można wdrożyć Public Key Pinning.
HTTP Public Key Pinning – HPKP
Połączenia TLS (Transport Layer Security), zapewniające poufność i integralność transmisji danych są dzisiaj wykorzystywane w wielu aplikacjach webowych. Podczas nawiązywania połączenia TLS przeglądarka internetowa weryfikuje między innymi to czy certyfikat SSL, który przesłał jej serwer, został wystawiony (podpisany) przez Urząd Certyfikacji któremu ona sama (przeglądarka) ufa . Jeżeli tak, pozwala na dalsze połączenie. Jeżeli nie, przerywa je i informuje użytkownika o powodach takiej decyzji.
W przeszłości zdarzyło się jednak kilka razy – czy to z powodu przełamania technicznych zabezpieczeń, czy też błędu ludzkiego – że Urząd Certyfikacji, któremu ufały przeglądarki wydał błędne certyfikaty. Certyfikaty te były następnie wykorzystywane przez atakujących do przeprowadzania ataków man-in-the-middle i wykradania prywatnych danych użytkowników. Z racji tego, że przeglądarki ufały wystawcom tych certyfikatów to nie blokowały połączeń i nie ostrzegały użytkowników, co sprawiło, że przeprowadzanie skutecznych ataków było ułatwione.
Między innymi przed takimi zagrożeniami może ustrzec nas mechanizm Public Key Pinning (PKP). Dzięki niemu możliwe jest poinformowanie przeglądarki o tym jakiej wartości klucza publicznego (a właściwie pola Subject Public Key Info (SPKI) – jeżeli w dalszej części wpisu będę pisał o kluczu publicznym to w domyśle będzie to pole SPKI) przesłanej przez serwer powinna spodziewać się podczas nawiązywania połączenia TLS. O tym dlaczego zdecydowano, że podpinane będą skróty z pola SPKI, a nie samych certyfikatów można przeczytać tutaj.
Do pewnego momentu mechanizm Public Key Pinning realizowany był poprzez wiązanie domen z kluczami publicznymi i zaszywanie tej informacji statycznie w kodach źródłowych przeglądarek (Firefox, Chrome). Rozwiązanie to znacznie ograniczało liczbę domen, które mogły skorzystać z PKP. W praktyce była to wąska grupa domen popularnych serwisów takich jak Facebook, Twitter czy Google.
Ukończony w kwietniu tego roku dokument RFC 7469 o nazwie Public Key Pinning Extension for HTTP opisuje dużo prostszą i o wiele bardziej skalowalną metodę wdrożenia i stosowania PKP, a właściwie to HPKP (od HTTP Public Key Pinning). Dokument ten definiuje nowy nagłówek protokołu HTTP o nazwie Public-Key-Pins dzięki któremu serwer może przekazać przeglądarce informacje o tym jakiej wartości klucza publicznego powinna spodziewać się (przez określony czas) podczas nawiązywania połączenia TLS z danym hostem. Jeżeli serwer przedstawi się innym niż wcześniej zadeklarowanym kluczem publicznym przeglądarka przerwie połączenie.
Struktura nagłówka Public-Key-Pins
Nagłówek Public-Key-Pins przyjmuje ogólną formę:
Public-Key-Pins: pin-sha256="public-key-sha256-base64"; max-age=expire-time; includeSubdomains; report-uri="report-uri"
gdzie:
- pin-sha256 to zakodowany w Base64 skrót SHA-256 (obecnie standard definiuje tylko tę funkcję skrótu jako możliwą do wykorzystania) z pola Subject Public Key Info. Standard wymaga, aby zawsze w nagłówku HTTP wartość ta znalazła się przynajmniej dwukrotnie. O tym dlaczego to takie ważne, za chwilę.
- max-age to także wymagane pole, jest to czas podawany w sekundach przez jaki przeglądarka powinna zapamiętać, że dostęp do danej strony powinien być możliwy tylko, gdy przynajmniej jeden certyfikat ze ścieżki certyfikacyjnej – przesłany przez serwer podczas nawiązywania połączenia TLS – będzie odpowiadał wskazanemu kluczowi publicznemu. O tym jakie są sugerowane wartości tego pola przeczytacie w dalszej części wpisu.
- includeSubdomains to opcjonalny parametr pozwalający na stosowanie mechanizmu także dla wszystkich subdomen danej domeny.
- report-uri to opcjonalny parametr pozwalający na zdefiniowanie adresu na który przeglądarka będzie mogła wysłać informacje w przypadku błędnej weryfikacji podpiętych wartości klucza. Mówiąc inaczej, to adres na który wysłane zostanie powiadomienie w przypadku wykrycia wszelkich prób podstawienia fałszywego certyfikatu SSL dla naszej strony. Zwracana przez przeglądarkę wartość to określona struktura w formacie JSON wśród której znajduje się między innymi: data i czas błędnej weryfikacji podpiętych kluczy, nazwa hosta i port z którym próbowano nawiązać połączenie oraz certyfikaty które przesłał serwer (niezgodne z tymi, których klucze publiczne zostały wskazane w nagłówku Public-Key-Pins). Co ciekawe możliwe jest, zamiast przekazywania nagłówka Public-Key-Pins, przekazanie przez serwer nagłówka Public-Key-Pins-Report-Only dzięki czemu przeglądarka będzie wysyłała raporty, ale nie będzie blokowała połączeń w przypadku wykrycia niezgodności (nie zapamięta Pinów).
Co warto wiedzieć przed wdrożeniem HPKP?
Z mechanizmem HPKP związanych jest kilka istotnych kwestii, które warto wiedzieć przed jego zastosowaniem. Pierwszą z ważnych informacji jest to, że HTTP Public Key Pinning jest mechanizmem z rodzaju trust-of-first-use (TOFU). Oznacza to, że podczas pierwszego połączenia z danym serwerem przeglądarka nie jest w stanie sprawdzić zadeklarowanych kluczy publicznych i może tutaj dojść do skutecznego ataku z podstawionym przez atakującego certyfikatem. Co gorsza, atakujący może także zmodyfikować nagłówek Public-Key-Pins i podstawić swoje klucze publiczne.
Drugą kwestią o której powinniśmy pamiętać jest to, że niewłaściwe wdrożenie HPKP może doprowadzić do zablokowania dostępu do strony. Jak może dojść do takiej sytuacji? Załóżmy, że zdefiniowaliśmy nagłówek Public-Key-Pins definiując tylko jeden skrót SHA-256 z klucza publicznego certyfikatu. Tylko z tego, który w danej chwili jest zainstalowany na naszej stronie. Przyjmijmy, że parametr max-age ustawiliśmy na 60 dni. Oznacza to, że przeglądarka użytkownika podczas jego pierwszej wizyty na naszej stronie zapamięta, że przez kolejne 60 dni ma odrzucać próby połączenia z naszą stroną w przypadku, gdy serwer przedstawi się innym niż zdefiniowanym przez nas kluczem publicznym.
Wszystko działa dobrze do momentu, gdy nie zajdzie potrzeba wymiany certyfikatu (np. z powodu kompromitacji klucza prywatnego). Co wtedy? W sytuacji, gdy zdefiniowaliśmy tylko jeden – wykorzystywany w momencie konfiguracji nagłówka PKP – skrót z klucza publicznego, który teraz musimy zastąpić innym, pojawia się problem. Przeglądarki zapamiętały dany Pin i przez 60 dni nie pozwolą użytkownikom na połączenie się z daną stroną jeżeli nie odnajdą właściwego klucza publicznego. Jak więc rozwiązać ten problem? Jak wspomniałem wcześniej standard wymaga, aby podczas definiowania nagłówka PKP pole pin-sha256 występowało przynajmniej dwa razy. Pierwszy raz dla aktualnego, produkcyjnie wykorzystywanego klucza (certyfikatu), drugi dla klucza zapasowego. Dzięki temu, gdy zajdzie potrzeba wymiany certyfikatu będziemy mogli wykorzystać zapasowe klucze do utworzenia nowego. Zalecane jest więc, żeby podczas wdrażania HPKP wygenerować dodatkową parę kluczy (lub nawet dwie pary), które będziemy mogli dopisać do nagłówka PKP. Zapasowe klucze powinno przechowywać się bezpiecznym miejscu, najlepiej offline.
Jak już wiecie, jednym z wymaganych pól nagłówka PKP jest max-age, które wskazuje przeglądarce na jak długo powinna zapamiętać przypięte skróty kluczy publicznych. Jaka wartość tego pola będzie więc odpowiednia? Standard proponuje nie przekraczać 5184000 sekund, czyli 60 dni. Jeżeli jednak pierwszy raz będziecie konfigurować HPKP warto zacząć od krótszych czasów. Dzięki temu będziecie mogli przetestować działanie mechanizmu bez ryzyka utraty dostępu do waszej strony na dłuższy czas.
Warto także zastanowić nad tym jakie certyfikaty będziemy chcieli przypiąć do naszej domeny. HPKP nie ogranicza nas tutaj do klucza publicznego certyfikatu serwera (końcowego). Możliwe jest także wskazanie wybranych certyfikatów pośrednich (subroot) i głównych (root) należących do wybranych Urzędów Certyfikacji. Wybór będzie niejako kompromisem pomiędzy poziomem bezpieczeństwa a poziomem ryzyka. W przypadku, gdy zdecydujemy o podpięciu
- Certyfikatu końcowego – otrzymujemy najwyższy poziom bezpieczeństwa, ale też najwyższy poziom ryzyka związany z zablokowaniem dostępu do strony. Jeżeli serwer nie prześle właściwego certyfikatu końcowego połączenie jest odrzucane. Dzięki temu eliminujemy ryzyko podstawienia fałszywego certyfikatu (np. błędnie wydanego przez tego samego wystawcę), ale też mamy najmniejsze pole manewru w przypadku, gdy zajdzie potrzeba wymiany aktualnego certyfikatu (generacja nowego certyfikatu na bazie kluczy zapasowych).
- Certyfikatu pośredniego – poziom bezpieczestwa jest już mniejszy, ale ryzyko maleje. Akceptowane będą wszystkie certyfikaty wydane przez dany certyfikat pośredni (wśród nich może znaleźć się certyfikat błędnie wydany dla naszej domeny), ale w przypadku problemów z aktualnym certyfikatem wystarczy wymiana na nowy wydany przez tego samego wystawcę.
- Certyfikatu głównego – najmniejszy poziom bezpieczeństwa, ale też i ryzyka. Akceptowane będą wszystkie certyfikaty, których ścieżka certyfikacji prowadzi do wskazanego certyfikatu głównego. W przypadku nieprzewidzianych sytuacji mamy większą możliwość wyboru certyfikatów do podmiany.
Jak więc widzicie poprzez HPKP możemy ograniczyć liczbę Urzędów Certyfikacji (opcja druga lub trzecia z powyższej listy), które będą uprawione do wydawania certyfikatów SSL dla naszej domeny.
Jak wdrożyć HPKP?
Pierwszą rzeczą, którą trzeba zrobić jest wyodrębnienie z certyfikatu informacji o kluczu publicznym (Subject Public Key Info), utworzenie z nich skrótu SHA-256 i na koniec zakodowanie w Base64. Wszystkie te operacje możemy wykonać za pomocą narzędzia OpenSSL i grupy poleceń:
openssl x509 -in certyfikat.pem -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
Jeżeli chcemy zrobić to samo dla klucza zapasowego należy użyć polecenia (tutaj nie mamy jeszcze certyfikatu więc operacje wykonujemy na wygenerowanym kluczu prywatnym):
openssl rsa -in klucz.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
W obu przypadkach wynikiem powinien być ciąg znaków wyglądający mniej więcej tak:
bgrbPjqldllylSI626f+4IcthL5YZh941jEKsfyp00U=
Następnie wystarczy dodać odpowiedni wpis do konfiguracji serwera, który w zależności od wykorzystywanego oprogramowania może nieznacznie się różnić.
Poniżej przykład dla Apache (wymagane jest, aby moduł mod_headers był włączony):
Header always set Public-Key-Pins "pin-sha256=\"bgrbPjqldllylSI626f+4IcthL5YZh941jEKsfyp00U=\"; pin-sha256=\"WeI1bVh0YZmWAt7/8bF6lLXn1am1n3vB3KosAQqqoz4=\"; max-age=2592000; includeSubDomains"
nginx (wymagany moduł ngx_http_headers_module):
add_header Public-Key-Pins 'pin-sha256="bgrbPjqldllylSI626f+4IcthL5YZh941jEKsfyp00U="; pin-sha256="WeI1bVh0YZmWAt7/8bF6lLXn1am1n3vB3KosAQqqoz4="; max-age=2592000; includeSubDomains';
oraz Lighttpd (wymagany mod_setenv):
setenv.add-response-header = ( "Public-Key-Pins" => "pin-sha256=\"bgrbPjqldllylSI626f+4IcthL5YZh941jEKsfyp00U=\"; pin-sha256=\"WeI1bVh0YZmWAt7/8bF6lLXn1am1n3vB3KosAQqqoz4="; max-age=2592000; includeSubDomains")
W przypadku IIS konfiguracji HPKP można dokonać definiując odpowiedni nagłówek poprzez HTTP Response Headers.
Wśród przeglądarek obecnie Firefox (od wersji 35) oraz Chrome (38) posiadają wsparcie dla HPKP.
Podsumowując, wdrożenie mechanizmu HTTP Public Key Pinning nie wymaga dużego nakładu pracy, a dzięki uważnej konfiguracji jesteśmy w stanie podnieść bezpieczeństwo użytkowników naszej strony bez narażania ich na utratę dostępu do niej. Szczególnie, gdy obok HPKP będziemy wykorzystywali, równie prosty w konfiguracji, mechanizm HSTS.
Pingback: NF.sec – Linux Security Blog - Konfiguracja nagłówków bezpieczeństwa na A+
Odnośnie wydania błednych certyfikatów :
w 2011 było włamanie do DigiNotar , holenderskiego CA I hakerzy sami sobie wygenerowali około kilkuset certyfikatów które uzyto później w atakach na userów z Iranu. Podobnie było z innym CA
StartCom shakowany był w 2008 I 2011. Próbowałem te nagłówki ustawić u siebie na nginx ale nie za bardzo mi zadziałało. Tzn użyłem złych skrótów aby zobaczyć czy przegladarka zareaguje . Nic się nie stało w IE I CHromie. Pewnie robie coś źle. Narazie Mój generator kodów qr https://qrcodego.com/pl ma żółty kolor https://securityheaders.io/
Tylko ostrożnie z max-age ;)
Dzięki za przystępne wyjaśnienie tematu i wyśmienity tutorial! Bardzo się przydało.
Może to „mag-age” dobrze by było poprawić – chociaż to też w sumie niezły test na uwagę czytających ;)
Dzięki jeszcze raz i pozdrawiam!
Dzięki za bardzo przydatny materiał, mam też kilka pytań:
1) Pierwsze wdrożenie HPKP: dajmy na to serwis działa po http, chcemy wdrożyć certyfikaty razem z tym nagłówikiem – wystarczy ustawić pin jednego z certyfikatów łańcucha a drugi losowy, np. nie używanego certyfikatu/klucza prywatnego, dobrze rozumiem?
2) W przypadku jak wyżej kiedy najlepiej/należy podmienić sumę z pola sumy tego nie używanego certyfikatu na tą z nowego certyfikatu? I czy należy zrobić to odejmując wartość parametru 'max-age’ od daty wygaśnięcia aktualnie działających certyfikatów?
3) Jeżeli mamy wdrożone HPKP, złożone z dwóch pin’ów, jeden z nich wskazuje na aktualnie działający cert (np. certyfikat serwera), drugi na cokolwiek innego, to jaka jest najlepsza procedura działania w przypadku wdrożenia nowego certyfikatu? Np. dostajemy nowe certy razem z certem serwera, na jego podstawie generujemy sumę i umieszczamy w miejsce wcześniej wrzuconej sumy zapasowego certyfikatu, tak? (to pytanie w sumie pokrywa się z tym wyżej)
Wielkie dzięki za odpowiedź i jeżeli coś namieszałem chętnie dopowiem.
1. Jeżeli zdecydujesz się „przypinać” certyfikat końcowy to warto na samym początku wygenerować sobie przynajmniej dwie pary kluczy, policzyć skróty (piny, sumy) i dodać oba do konfiguracji. Na bazie pierwszej pary kluczy uzyskujesz certyfikat i on działa na stronie. Drugą parę kluczy trzymasz w bezpiecznym miejscu. W przypadku, gdy coś wydarzy się z certyfikatem i nie będziesz mógł go wykorzystywać sięgasz po drugą parę kluczy i uzyskujesz dla nich certyfikat. Jednoczenie generujesz kolejną parę kluczy które stają się nowymi zapasowymi. Dzięki takim działaniom nie doprowadzisz do zablokowania dostępu do swojej strony użytkownikom których przeglądarki zapamiętały na określony czas wskazane przez Ciebie skróty.
2. W konfiguracji zawsze powinieneś mieć dwa skróty. Nie podmieniasz tego 1 do 1. Przyjmijmy, że na starcie ustawiasz dwa skróty A i B. A jest aktualnie wykorzystywanym, a B zapasowym. W przypadku „awarii” A, B staje się aktualnie wykorzystywanym. Dodatkowo dodajesz skrót C, który staje się zapasowym. I taki schemat powtarza się przy każdej wymianie certyfikatu.
3. Odpowiedź jest chyba wyżej :)
Jeżeli coś jeszcze jest nie jasne to pisz :)