Kompresja — ból głowy protokołu TLS

Wstęp

Protokół TLS został zaprojektowany w celu zapewnienia bezpieczeństwa komunikacji w sieci komputerowej. Jest powszechnie wykorzystywany w aplikacjach webowych, zwłaszcza jako kluczowy element protokołu HTTPS stanowiącego podstawę funkcjonowania współczesnego internetu. Oprócz sztandarowego szyfrowania standard TLS oferuje inne funkcjonalności. Jedną z nich jest kompresja danych. Jakby o tym pomyśleć to na pierwszy rzut oka wydaje się to świetną opcją — zmniejsza narzut danych, dzięki czemu odciąża łącze. Same plusy. Dlaczego w takim razie analizując pakiety TLS, np. w Wiresharku, niemal na pewno ujrzymy coś takiego?

Compression Methods widziane w Wiresharku

Dlaczego współczesne implementacje protokołu TLS zarzuciły korzystanie z kompresji danych, a najnowsza, wydana w 2018 roku, wersja TLS 1.3 całkowicie przestała wspierać kompresję, zachowując stosowne pola tylko ze względu na kompatybilność wsteczną?

Historia pewnej zbrodni, czyli atak CRIME

O potencjalnych problemach z bezpieczeństwem mechanizmu kompresji wbudowanego w protokół SSL/TLS teoretyzował już w 2002 roku John Kelsey, jednak wtedy uznawano to tylko za teoretyczne zagrożenie i temat ucichł aż to kolejnej dekady. W 2011 roku Adam Langley wymyślił zarys teoretyczny ataku, który został w praktyce zaprezentowany przez Juliana Rizzo and Thaia Duonga rok później, a sam atak dostał numer CVE-2012-4929. O CRIME zrobiło się głośno, wskutek czego dosyć szybko zanikło stosowanie kompresji TLS (pomimo faktu, iż do praktycznego jego wykorzystania musiały zajść określone okoliczności), a ostatecznie sam mechanizm został wycofany w TLS 1.3.

Uroki kompresji bezstratnej

Aby zrozumieć mechanizm działania ataku, najpierw należy zrozumieć, w jaki sposób działa bezstratna kompresja danych. Na szczęście jest to stosunkowo proste. Algorytmy kompresji bezstratnej wyszukują redundantne dane, a następnie starają się je zapisać w wersji skrótowej. Łopatologiczny przykład obrazujący powyższą teorię:

Mamy wiadomość QQQQQWEEEERTY. Użyjmy bardzo prostego algorytmu Run-Length Encoding (RLE), algorytm ten dokonuje kompresji tej wiadomości zamieniając powtarzające się ciągi na ich skrótowy zapis, zmniejszając tym samym ilość danych potrzebną do reprezentacji wiadomości. Wiadomość ta może być zapisana jako np. 5QW4ERTY. No ale jak to się ma do bezpieczeństwa szyfrowania?

Załóżmy, że przesyłamy wartość (może to być np. wartość ciasteczka) : QQQQWERTY Po skompresowaniu naszym przykładowym algorytmem wartość ciasteczka będzie wynosić: 4QWERTY

Jeśli poprzedzimy dane np. znakiem Z, będzie się to prezentować w poniższy sposób:

CiasteczkoSkompresowane
ZQQQQWERTYZ4QWERTY

Rozważmy teraz przypadek, w którym poprzedzamy nasze ciasteczko znakiem Q:

CiasteczkoSkompresowane
QQQQQWERTY5QWERTY

Zauważyć zatem można, że jeśli poprzedzimy ciasteczko znakiem Q skompresowana wartość będzie krótsza niż jeśli poprzedzimy je znakiem Z. Dzięki czemu poznaliśmy pierwszy znak ciasteczka, czyli Q. Następnie możemy poprzedzać dane ciągami typu QZ, QQ etc. i poznać w ten sposób resztę ciasteczka.

Kiedy już jesteśmy zapoznani z tą teorią, możemy się łatwo domyślić, że jeśli ktoś będzie umiejętnie dobierał ciągi, którymi będziemy poprzedzać nasze dane, to będziemy w stanie poznać całą przesyłaną wiadomość. Zależność ta jest prawdziwa również dla danych, które po kompresji ulegają zaszyfrowaniu.

Właściwy atak CRIME

Przechodząc do właściwego konceptu ataku: jeśli atakujący w jakiś sposób doprowadzi do wywołania u ofiary złośliwego kodu JavaScript (przykładowo wykorzystując socjotechnikę, tak aby ofiara weszła na stronę przygotowaną przez atakującego), który wykonuje odpowiednio spreparowane zapytania do danej aplikacji webowej, do których przeglądarka automatycznie załącza ciasteczka. Atakujący nie jest w stanie podejrzeć wartości ciasteczka, ponieważ jest ona zaszyfrowana, jednak jest on w stanie poprzedzić jego wartość jakimś znakiem bądź ciągiem znaków i obserwować długość wynikowego zapytania, by na tej podstawie ustalić całe ciasteczko, z którym jest w stanie np. wykonać atak session hijacking. Atakujący jednak musi w tym celu również podsłuchiwać ruch sieciowy, więc de facto jest to atak typu MITM (Man In The Middle). Należy zaznaczyć, że mimo tego ograniczenia, atak ten mógłby stanowić potencjalnie groźne narzędzie w rękach bardziej zaawansowanych threat actorów. “Mógłby”, ponieważ jak było wspomniane wcześniej, niemal całkowicie zarzucono stosowanie kompresji TLS.

Kiedy nawet czas jest przeciwko Tobie… atak TIME

Jak wcześniej zostało wspomniane, atak CRIME mimo potencjalnych groźnych następstw ma swoje ograniczenia. Jednak niedługo po jego opublikowaniu został zaproponowany atak obchodzący jego ograniczenia — TIME (Timing Info-leak Made Easy) attack. Skierowany on jest nie w zapytania HTTPS, ale w jego odpowiedzi. Jak nazwa wskazuje, opiera się on na mierzeniu czasu odpowiedzi na zapytanie. Opisane wcześniej “trafienie” kolejnego bajtu wiadomości będzie skutkowało mniejszą wiadomością, a co za tym idzie krótszym czasem odpowiedzi. Jako główna zaleta takiego pośredniego rozwiązania, wymieniana jest możliwość pomiaru czasu z pomocą samego JavaScriptu, dzięki czemu atakujący nie musi podsłuchiwać ruchu sieciowego. Wydaje się to być ciężkie do osiągnięcia, w końcu jeden bajt robi mikroskopijną wręcz różnicę przy możliwości przesyłania ich setek tysięcy i więcej w ciągu jednej sekundy. Tutaj z pomocą przychodzi protokół TCP. W przypadku kiedy dane, które chcemy przesłać, wykraczają ponad rozmiar okna (Window size) przekazany uprzednio przez odbiorcę, musimy poczekać na otrzymanie pakietu ACK, zanim rozpoczniemy dalszą transmisję danych. Oczekiwanie na ACK wydłuża RTT (Round Trip Time) latency. Należy pamiętać, że atakujący ma pełną kontrolę nad rozmiarem przesyłanych danych. Mogą więc przesyłać dane dokładnie takiego rozmiaru, że dorzucenie jednego bajta przekroczy rozmiar okna i zauważalnie wydłuży opóźnienie RTT. Na mierzony czas wpływa losowy szum, jednak ten problem można wyeliminować, wysyłając np. 10 pakietów i brać najkrótszy z otrzymanych czasów. Atak został zaprezentowany w praktyce na europejskiej konferencji BlackHat w 2013 roku.

Problemy bardziej codzienne niż stare podatności

Brak znaczących efektów kompresji danych

W obecnych czasach zawartość aplikacji webowych i tak jest w dużym stopniu kompresowana, minifikowana etc. Z tego względu dodatkowa kompresja na poziomie protokołu TLS nie przynosi znaczących efektów. Czasem taka dodatkowa kompresja może zadziałać odwrotnie, niż oczekiwano i zwiększyć rozmiar danych. Jak widać, jest to po prostu gra nie warta świeczki.

Komplikowanie implementacji protokołu

Dodatkowe mechanizmy, które musi obsłużyć protokół TLS, prowadzą do dalszego skomplikowania jego implementacji i problemów z kompatybilnością i interoperacyjnością. Różne implementacje mogą wykorzystywać różne algorytmy konwersji, konwersja może być problematyczna w połączeniu z innymi rozszerzeniami protokołu etc., więc jest to pozbawione sensu przy niewielkich zyskach z utrzymywania mechanizmów odpowiedzialnych za kompresję danych.

Podsumowanie

Jak widać kompresja danych, stanowiąca element standardu protokołu TLS, tylko pozornie wydawała się być dobrym rozwiązaniem, a wynika z niej więcej problemów niż pożytku. Dlatego słusznie wyłączono ten mechanizm ze standardu najnowszej wersji protokołu TLS 1.3. Kompresję pozostawmy innym narzędziom, a pozwólmy protokołom i algorytmom zapewniającym bezpieczeństwo robić to co do nich należy, bez nakładania na nich dodatkowych obowiązków.