Rozwój oprogramowania

Ataki czasowe w PHP: Praktyczny przykład - dlaczego `hash_equals()` ma znaczenie

Chcesz poznać realistyczny przykład ataku czasowego na tokeny API w Wordpress/Laravel/PHP? Omówimy to tutaj, w tym wskazówki i najlepsze praktyki dotyczące ataków czasowych.

Wielu programistów polega na prostym porównaniu przy użyciu == lub == podczas sprawdzania kluczy API, tokenów lub innych tajnych ciągów. Na pierwszy rzut oka wygląda to rozsądnie - ale jest pewien haczyk: różnice czasowe w porównaniu mogą zostać wykorzystane przez atakujących.

W tym artykule opisano realistyczny przykład tego, jak koncepcyjnie działa atak czasowy.

Prawdziwy scenariusz (uproszczony przykład)

Wyobraź sobie, że wewnętrzna mikrousługa akceptuje żądania z tokenem parametru zapytania i porównuje go z tokenem API GitHub przechowywanym w środowisku.

Niezabezpieczony kod (uproszczony):

// insecure.php (uproszczony przykład)$expected = getenv('GITHUB_API_TOKEN');// np. 'V1cHt2S67DADJIm9sX9yzCc272EkSC'$provided = $_GET['token'] ?? '';if ($provided == $expected) { // grant access} PHP  

Wyjaśnię, dlaczego jest to problematyczne w następnej sekcji.

Jak działa atak czasowy?

Jeśli implementacja porównywania tokenów porównuje znak po znaku i przerywa natychmiast przy pierwszym błędzie, to żądanie z poprawnym pierwszym znakiem trwa średnio nieco dłużej.

Prawidłowy pierwszy znak prowadzi zatem średnio do nieco dłuższego czasu przetwarzania niż nieprawidłowy pierwszy znak. To samo dotyczy drugiego znaku i tak dalej.

Atakujący wykorzystuje to, testując wszystkie możliwe znaki dla każdej pozycji, bardzo często mierząc czasy odpowiedzi i analizując je statystycznie. Znak z największą średnią/proporcjonalnie dłuższym czasem jest prawdopodobnie poprawny. W ten sposób token jest rekonstruowany krok po kroku.

Dlaczego hash_equals() jest właściwym wyborem?

PHP zapewnia funkcję porównywania ciągów znaków o stałym czasie działania z hash_equals(). Gwarantuje ona, że czas wykonania porównania nie zależy od wspólnych prefiksów - o ile oba ciągi są tej samej długości.

Bezpieczna alternatywa (dla Laravel, PHP, Wordpress):

// secure.php$expected = getenv('GITHUB_API_TOKEN'); $provided = $_GET['token'] ?? '';if (hash_equals($expected, $provided)) { // udziel dostępu} PHP  

Uwaga: hash_equals() jest "niezależne od czasu" tylko wtedy, gdy długości są równe. Dobrą praktyką jest projektowanie tokenów tak, aby ich długość była stała (np. HMAC, UUID o stałej długości lub losowe ciągi Base64 o stałej długości).

Czy wiesz, że? Tylko UUID-V4 jest całkowicie losowo generowany i dlatego jest często używany do tokenów bezpieczeństwa. Ponieważ nie zawiera informacji związanych z czasem lub urządzeniem, oferuje wysoki stopień nieprzewidywalności, a tym samym chroni przed atakami opartymi na wzorcach lub przewidywaniach. Właśnie dlatego UUID-V4 jest preferowanym wyborem, jeśli chodzi o bezpieczne, trudne do odgadnięcia identyfikatory.

Dodatkowe środki obrony - więcej niż tylko hash_equals()

  1. Stała długość tokena
    Używaj tokenów o stałej długości, np. UUID-V4. Jeśli atakujący odgadnie długość, jest to mniej pomocne, o ile same znaki są bezpieczne.
  2. Ograniczenie szybkości
    Ogranicz liczbę żądań na adres IP lub konto. Ataki czasowe wymagają wielu pomiarów; ograniczenie szybkości znacznie zwiększa wysiłek i koszty dla atakującego.
  3. Rejestrowanie i monitorowanie
    Rozpoznawaj nietypowe wzorce (wiele prób użycia tokenów, systematyczne zmiany niektórych parametrów) i reaguj automatycznie.
  4. Sól / HMAC / podpisy
    Używaj podpisanych tokenów (HMAC z tajnym kluczem) lub tokenów OAuth zamiast otwartego porównywania surowych kluczy statycznych.
  5. TLS/HTTPS
    Szyfrowanie chroni kanały komunikacji - chociaż nie zapobiega atakom czasowym na wewnętrzne elementy serwera, jest to podstawowy wymóg.
  6. Rotacja tokenów
    Krótka żywotność tokenów zmniejsza korzyści wynikające z ich naruszenia.

Wnioski

Ataki czasowe nie są problemem czysto teoretycznym - zostały one zbadane w praktyce i mogą mieć poważne konsekwencje, zwłaszcza dla publicznie dostępnych punktów końcowych. Dobra wiadomość: dla programistów PHP/Laravel/Wordpress pierwszy środek zaradczy jest bardzo prosty:

  • Zamień bezpośrednie porównania wrażliwych ciągów na hash_equals().
  • Zapewnij stałą długość tokenów, ograniczenie szybkości, logowanie i bezpieczne zarządzanie tokenami.
O autorze
Założyciel i CEO Langmeier Software
Nie chcę niczego komplikować. Nie chcę tworzyć najlepszego oprogramowania biznesowego. Nie chcę znaleźć się na liście najlepszych technologii. Ponieważ nie o to chodzi w aplikacjach biznesowych. Chodzi o to, aby upewnić się, że Twoje dane są bezbłędnie chronione. Chodzi też o to, by wszystko działało płynnie, a Ty zachowałeś pełną kontrolę i mogłeś skupić się na rozwoju swojej firmy. Prostota i niezawodność to moje główne zasady, które inspirują mnie każdego dnia.
 
Szukaj dalej:
PHP, Świadomość bezpieczeństwa, Cyberbezpieczeństwo
Powiązane artykuły