Ataki czasowe w kodzie na poziomie aplikacji
Proszę poznać sposoby, w jakie czas porównywania ciągów ujawnia sekrety, oraz dowiedzieć się, jak zapobiega temu porównywanie w czasie stałym.
Ataki czasowe w kodzie na poziomie aplikacji to bezpłatna lekcja Cryptology Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Cryptology Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Cryptology Academy zawiera 4 lekcji w sumie.
Porównywanie ciągów znaków w czasie zależnym od danych
Standardowy operator równości ciągów znaków w większości języków programowania kończy działanie natychmiast po znalezieniu niezgodności. Python's == w przypadku obiektów bytes, C's strcmp oraz Java's String.equals zwracają wynik od razu po znalezieniu pierwszego różniącego się bajtu. W przypadku zwykłego porównywania ciągów jest to optymalizacja, ale podczas porównywania wartości tajnych, takich jak tagi MAC lub hasła, tworzy mierzalny kanał boczny oparty na czasie, który ujawnia informacje.
Pomiar czasu porównywania HMAC
Napastnik mierzy czas potrzebny na porównanie przesłanego znacznika HMAC z poprawną wartością. Przesłanie HMAC, w którym pierwszy bajt jest poprawny, trwa nieco dłużej niż przesłanie HMAC z niepoprawnym pierwszym bajtem, ponieważ wykonywane jest jedno dodatkowe porównanie bajtu. Przesyłając wiele wartości z każdą możliwą wartością pierwszego bajtu i mierząc czasy odpowiedzi, napastnik ustala poprawny pierwszy bajt. Następnie powtarza ten proces bajt po bajcie, aż odtworzy cały znacznik.
Precyzja pomiaru czasu w praktycznych atakach
Współczesne ataki czasowe w sieci potrafią wykrywać różnice czasowe rzędu dziesiątek do setek nanosekund przez Internet. Porównanie 32-bajtowego HMAC, w którym każdy poprawny bajt dodaje około 10–100 ns czasu przetwarzania, dostarcza mierzalnego sygnału, jeśli wykona się wystarczająco wiele powtórnych pomiarów, aby uśrednić wahania opóźnień sieciowych. W sieci lokalnej można wykorzystać nawet różnice rzędu pojedynczych nanosekund, stosując odpowiednio dużą liczbę próbek statystycznych.
Podatność operatora == w Pythonie
Porównywanie znaczników MAC za pomocą == jest niebezpieczne: wyrażenie mac == submitted_mac zwraca True lub False zależnie od pozycji pierwszej niezgodności. Napastnik, przesyłając tysiące specjalnie przygotowanych znaczników i mierząc czasy odpowiedzi, może odtworzyć oczekiwany znacznik bajt po bajcie. Ta podatność występowała w produkcyjnych aplikacjach internetowych, które nieprawidłowo implementowały porównywanie tokenów sesji lub kluczy API bez użycia funkcji działających w stałym czasie.
hmac.compare_digest w Pythonie
Funkcja hmac.compare_digest(a, b) w Pythonie porównuje dwie wartości bajtowe lub tekstowe w stałym czasie, niezależnie od miejsca wystąpienia pierwszej niezgodności. Zaimplementowano ją w języku C, aby zapewnić działanie w stałym czasie nawet przy narzucie związanym z interpretacją bajtkodu Pythona. Do porównywania znaczników MAC, tokenów sesji, kluczy API oraz wszelkich wartości, których ujawnienie informacji czasowych byłoby niebezpieczne, należy zawsze używać hmac.compare_digest.
CRYPTO_memcmp w OpenSSL
OpenSSL udostępnia funkcję CRYPTO_memcmp(a, b, length) do porównywania pamięci w stałym czasie. W przeciwieństwie do memcmp zawsze przetwarza wszystkie length bajtów, niezależnie od wcześniejszych niezgodności. Zwraca zero, jeśli wartości są równe, oraz wartość niezerową, jeśli się różnią. Należy zawsze porównywać pełną oczekiwaną długość: porównywanie wartości o różnej długości tylko do długości krótszej może nadal ujawniać informacje o długości. W kodzie C/C++ korzystającym z OpenSSL należy używać CRYPTO_memcmp w każdym porównaniu o znaczeniu dla bezpieczeństwa.
Ataki czasowe na RSA: Bleichenbacher
Ataki czasowe wykraczają poza porównywanie ciągów znaków. Atak Bleichenbachera z 2006 roku na odszyfrowywanie RSA PKCS#1 v1.5 wykazał możliwość przeprowadzenia praktycznego ataku z wykorzystaniem wyroczni czasowej na implementacje SSL/TLS. Czas trwania operacji z użyciem klucza prywatnego RSA różnił się zależnie od tego, czy odszyfrowana wartość zawierała poprawne dopełnienie PKCS#1. Przesyłając tysiące specjalnie przygotowanych szyfrogramów, napastnicy mogli odzyskać prywatne klucze RSA. Doprowadziło to do opracowania RSA-OAEP i stałoczasowych implementacji RSA.
Ataki czasowe na pamięć podręczną AES
Implementacje AES korzystające z tablic wyszukiwania, często stosowanych ze względów wydajnościowych, uzyskują dostęp do różnych elementów tablicy zależnie od klucza i tekstu jawnego. Trafienia i chybienia pamięci podręcznej powodują mierzalne różnice czasowe, które ujawniają informacje o tym, do których elementów tablicy uzyskano dostęp. Ten kanał boczny może ujawnić klucze AES. Obrona polega na używaniu implementacji AES, które nie opierają się na wyszukiwaniu w tablicach, takich jak sprzętowe instrukcje AES-NI lub programowe implementacje bit-sliced.
Zasady implementacji w stałym czasie
Pisanie kodu działającego w stałym czasie wymaga unikania: rozgałęzień warunkowych zależnych od danych tajnych (należy stosować bezgałęziowy wybór z maskowaniem), wzorców dostępu do pamięci zależnych od danych tajnych (należy unikać tablic wyszukiwania indeksowanych wartościami tajnymi) oraz operacji, których opóźnienie zależy od wartości tajnych (np. dzielenia na niektórych procesorach). Kompilatory mogą optymalizować konstrukcje stałoczasowe, dlatego w krytycznych sekcjach może być konieczne użycie kodu asemblera lub dostępów do pamięci oznaczonych jako volatile.
AEAD eliminuje porównywanie MAC na poziomie aplikacji
Najlepszą ochroną przed atakami czasowymi na porównywanie MAC jest używanie trybów AEAD (GCM, ChaCha20-Poly1305) i przekazanie weryfikacji MAC bibliotece kryptograficznej. Implementacje bibliotek wewnętrznie wykonują weryfikację w stałym czasie. Jeśli tryb AEAD jest używany prawidłowo (odszyfrowywanie kończy się niepowodzeniem przy każdej modyfikacji, a odszyfrowywanie nigdy nie następuje przed weryfikacją znacznika), kod aplikacji nie musi porównywać znaczników MAC, co całkowicie eliminuje tę podatność czasową.
Testowanie podatności czasowych
Testowanie podatności czasowych wymaga analizy statystycznej rozkładów czasów odpowiedzi. Narzędzia takie jak tlsfuzzer, skrypty do testowania ataków czasowych oraz framework dudect pomagają wykrywać różnice czasowe w implementacjach kryptograficznych. Test t przeprowadzony na próbkach czasów odpowiedzi dla danych wejściowych, które powinny dawać jednakowe czasy, może ujawnić różnice istotne statystycznie. Możliwe są wyniki fałszywie ujemne, dlatego oprócz testowania niezbędny jest także przegląd kodu pod kątem działania w stałym czasie.
Porównywanie w stałym czasie
Jakiej funkcji Pythona należy używać do bezpiecznego porównywania znacznika HMAC, aby zapobiec atakom czasowym?
Podsumowanie ataków czasowych
Podsumowanie ataków czasowych: porównywanie ciągów znaków z wcześniejszym zakończeniem ujawnia tajne wartości bajt po bajcie przez różnice w czasach odpowiedzi; przy odpowiedniej liczbie próbek można je mierzyć przez sieć; w Pythonie do porównań w stałym czasie należy używać hmac.compare_digest, a w OpenSSL — CRYPTO_memcmp; ataki czasowe na dopełnienie RSA mogą naruszyć bezpieczeństwo kluczy prywatnych (należy używać stałoczasowego RSA i OAEP); ataki czasowe wykorzystujące wyszukiwanie w tablicach AES ujawniają bity klucza (należy używać AES-NI lub implementacji bit-sliced); weryfikacja przez bibliotekę AEAD eliminuje potrzebę porównywania MAC na poziomie aplikacji.
Często zadawane pytania
Czy lekcja „Ataki czasowe w kodzie na poziomie aplikacji” jest bezpłatna?
Tak — pełny tekst „Ataki czasowe w kodzie na poziomie aplikacji” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Cryptology Academy, przejdź na CoddyKit PRO. Kurs Cryptology Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Ataki czasowe w kodzie na poziomie aplikacji”?
Proszę poznać sposoby, w jakie czas porównywania ciągów ujawnia sekrety, oraz dowiedzieć się, jak zapobiega temu porównywanie w czasie stałym. Ćwiczysz Cryptology Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Cryptology Academy?
Nie wymagamy żadnego doświadczenia. Cryptology Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Ataki czasowe w kodzie na poziomie aplikacji”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Cryptology Academy?
Tak. Każda lekcja Cryptology Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Ataki padding oracle w szczegółach
- Ataki typu replay i luki związane z ponownym użyciem nonce
- Ataki czasowe w kodzie na poziomie aplikacji
- Najczęstsze wzorce błędnego użycia kryptografii