race condition
| Po angielsku | race condition |
|---|---|
| Którą formą pisać | po angielsku Polski odpowiednik istnieje, ale w praktyce nikt tak nie pisze. Zostaw angielski, polski trzymaj jako synonim. |
| Inne formy | sytuacja wyścigu (nie potwierdzono w źródłach) · warunek wyścigu (nie potwierdzono w źródłach) · TOCTOU (podtyp, nie synonim) |
| Kategoria | Podatności i klasy błędów |
Race condition to podatność, w której fragment kodu potrzebuje chwilowego, wyłącznego dostępu do współdzielonego zasobu, ale istnieje okno czasowe, w którym ten zasób może zmienić inny, równolegle działający kod [1]. Wynik operacji zależy wtedy od kolejności zdarzeń, a nie od logiki programu. MITRE opisuje błąd jako naruszenie dwóch własności: wyłączności, czyli tego, że nikt inny nie zmienia zasobu w trakcie operacji, oraz atomowości, czyli tego, że ta sama sekwencja nie wykonuje się równolegle na tym samym zasobie [1]. Słabość ma w katalogu CWE numer CWE-362.
Jak działa
Aplikacja internetowa obsługuje wiele żądań jednocześnie. Jeśli kod najpierw sprawdza warunek, na przykład „kupon nie został użyty”, a dopiero potem zapisuje zmianę, między tymi krokami powstaje przerwa. PortSwigger nazywa ją oknem wyścigu (race window) i opisuje jako okres, w którym możliwa jest kolizja, np. milisekundy między dwiema operacjami na bazie danych [3]. Dwa żądania wysłane w tym samym momencie mogą oba przejść sprawdzenie, zanim którekolwiek zapisze wynik.
Schemat poniżej pokazuje taki przebieg na przykładzie jednorazowego kodu rabatowego w sklepie example.com.
Okno bywa bardzo krótkie, ale atakujący potrafią je trafić. PortSwigger opisuje technikę single-packet attack, pokazaną po raz pierwszy na konferencji Black Hat USA 2023: w protokole HTTP/2 jeden pakiet TCP dostarcza 20–30 żądań naraz, co usuwa wpływ opóźnień sieci [3]. Według tego samego źródła do wywołania błędu często wystarczą dwa żądania [3].
Odmiany
Najczęściej wykorzystywaną odmianą jest TOCTOU (time-of-check, time-of-use). MITRE definiuje ją jako sytuację, w której produkt sprawdza stan zasobu przed jego użyciem, a stan ten może się zmienić między sprawdzeniem a użyciem [2]. CWE-367 jest w katalogu podrzędne wobec CWE-362 [2].
| Odmiana | Co się ściga | Przykład |
|---|---|---|
| Przekroczenie limitu (limit overrun) | kilka żądań z tym samym uprawnieniem | wielokrotne użycie karty podarunkowej [3] |
| TOCTOU na pliku | sprawdzenie pliku i jego użycie [2] | plik wgrany przed walidacją [4] |
| Wyścig wątków w pamięci | dwa wątki na jednej zmiennej | operacja x++ nie jest atomowa [1] |
PortSwigger wymienia typowe cele ataków na limity: wielokrotne użycie karty podarunkowej, wielokrotne ocenienie produktu, wypłatę ponad saldo konta, ponowne użycie rozwiązania CAPTCHA i obejście ograniczenia liczby prób logowania [3]. Te przypadki łączy to, że są błędem logiki biznesowej: kod działa poprawnie dla jednego żądania, a zawodzi dla kilku równoległych [3]. Ostatni przykład oznacza, że rate limiting i ochrona przed atakami brute force same mogą mieć race condition.
W WordPressie
Najgroźniejszy scenariusz dla stron na WordPressie dotyczy przesyłania plików. Sekurak w 2016 roku opisał aplikację w PHP, która przyjmowała tylko obrazy, ale zapisywała plik do katalogu publicznego i dopiero potem go sprawdzała [4]. Atakujący wysyłał setki równoległych żądań: jedne wgrywały skrypt PHP, inne próbowały go uruchomić, zanim walidacja zdążyła plik usunąć [4]. Sekurak definiuje taki atak jako wykonanie zapytania w czasie krótszym, niż trwa weryfikacja warunków danej akcji [4]. Skutkiem jest wgranie dowolnego pliku (arbitrary file upload) i w dalszej kolejności webshell. Ten sam wzorzec, czyli plik tymczasowy w katalogu dostępnym z sieci, może wystąpić w każdej wtyczce obsługującej pliki; zob. podatność we wtyczce.
Rdzeń WordPressa ma własny mechanizm przeciw równoległym operacjom. Metoda WP_Upgrader::create_lock() tworzy blokadę w tabeli opcji [6]. Używa zapytania INSERT IGNORE, które nie powiedzie się, jeśli blokada o tej nazwie już istnieje; domyślny czas respektowania istniejącej blokady to jedna godzina [6]. To przykład obrony opartej na ograniczeniu unikalności w bazie, a nie na sprawdzeniu warunku w PHP.
Ta sama zasada sprawdza się we własnym kodzie wtyczki. Sprawdzenie i zmianę warto połączyć w jednym zapytaniu, jak w ilustracyjnym fragmencie poniżej.
// sprawdzenie i zapis w jednym zapytaniu: drugi wyścig nie zmieni żadnego wiersza
$zmienione = $wpdb->query( $wpdb->prepare(
"UPDATE {$wpdb->prefix}kupony SET uzyty = 1 WHERE kod = %s AND uzyty = 0",
$kod
) );
if ( 1 !== $zmienione ) {
return new WP_Error( 'kupon', 'Kupon został już wykorzystany.' );
}
Zagrożenia i skutki
MITRE wymienia jako skutki race condition wyczerpanie zasobów, awarie systemu, ujawnienie danych w połączeniu z przewidywalnymi nazwami plików i słabymi uprawnieniami oraz eskalację uprawnień w kodzie zarządzającym uwierzytelnianiem lub stanem [1]. W aplikacjach internetowych dochodzą straty finansowe: podwójne rabaty, wypłaty ponad saldo i wielokrotne nagrody [3].
Najcięższym skutkiem jest zdalne wykonanie kodu (RCE). Tak kończy się wyścig, w którym plik z kodem PHP jest dostępny przez chwilę pod publicznym adresem [4]. Awarie wynikające z równoczesnego dostępu wielu przepływów do zasobu mogą też działać jak atak odmowy usługi (DoS) [1].
Jak się bronić
Podstawą jest atomowość operacji wrażliwych. PortSwigger zaleca, by punkty końcowe zmieniające stan korzystały z mechanizmów współbieżności bazy danych, na przykład sprawdzały zgodność płatności i potwierdzały zamówienie w jednej transakcji [3]. Jako obronę warstwową wymienia ograniczenia unikalności kolumn, a także zaleca, by nie zabezpieczać jednej warstwy danych drugą: sesja nie nadaje się do ochrony limitów w bazie [3].
MITRE zaleca prymitywy synchronizacji wokół sekcji krytycznych, operacje atomowe na współdzielonych zmiennych, ograniczanie współdzielonych zasobów i poprawne użycie muteksów bez ryzyka zakleszczenia [1].
Dla plików zasada jest prosta: wszystkie właściwości bezpieczeństwa sprawdza się, zanim plik trafi do katalogu dostępnego z sieci [4]. Drugą warstwą jest blokada wykonywania PHP w katalogu uploads, która nie pozwala uruchomić skryptu nawet wtedy, gdy wyścig się powiedzie.
Historia i przykłady
CERT Polska 19 maja 2026 roku opublikował informację o podatnościach w produktach firmy Sparx Systems [5]. Jedna z nich, CVE-2026-42099, to race condition w punkcie końcowym /data_api/dl_internal_artifact.php serwera Pro Cloud Server, sklasyfikowany jako CWE-362 [5]. Aplikacja zapisywała tymczasowo na dysk treść pobraną przez parametr guid i usuwała plik po przetworzeniu [5]. Przy wolnej transmisji albo dużym pliku atakujący mógł wysłać drugie żądanie i uruchomić złośliwy plik PHP przed jego usunięciem, co prowadziło do zdalnego wykonania kodu [5]. Podatne były wszystkie wersje do 6.1 włącznie [5].
Wśród przykładów w katalogu CWE-362 MITRE wymienia CVE-2021-1782: niewłaściwe blokowanie (CWE-667) prowadziło tam do race condition, a podatność była według katalogu KEV agencji CISA wykorzystywana w realnych atakach [1].
Przypisy
6- [1] CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') · MITRE
- [2] CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition · MITRE
- [3] Race conditions — Web Security Academy · PortSwigger
- [4] Atak Race Condition – przykład zastosowania w aplikacji webowej · Sekurak
- [5] Podatności w produktach firmy Sparx Systems (CVE-2026-42096 – CVE-2026-42100) · CERT Polska
- [6] WP_Upgrader::create_lock() · WordPress.org