-15% na nową książkę sekuraka: Wprowadzenie do bezpieczeństwa IT. Przy zamówieniu podaj kod: 10000

GitHub publikuje szczegóły błędu typu race condition w zarządzaniu sesjami

19 marca 2021, 11:27 | Aktualności | 0 komentarzy

W pierwszej połowie marca 2021 u wszystkich użytkowników GitHuba wymuszono ponowne zalogowanie się, po tym jak część użytkowników zostawała nieoczekiwanie zalogowana na cudze konta. Wczoraj GitHub opublikował szczegóły tego błędu.

Okazało się, że przyczyną błędu był race condition w zarządzaniu sesjami.

W skrócie: interfejs GitHuba jest napisany w oparciu o framework Ruby on Rails, który z kolei jest uruchomiony na serwerze Unicorn Rack. Aplikacja ta jest wielowątkowa, a zadaniem jednego z wątków jest obsługa wyjątków. Jak informuje GitHub, zespół miał świadomość, że pewne komponenty tego kodu nie są przystosowane do działania na wielu wątkach (nie są więc thread-safe), choć nie sądzono, że będzie to miało wpływ na tę część, którą widzą użytkownicy. Ryzykiem, jakiego się spodziewano, były co najwyżej błędne informacje zostawione w wewnętrznych logach.

Gdy serwera Rack obsługuje żądania http, wykorzystuje obiekt env zawierający odpowiednik zmiennych środowiskowych, choć de facto mogą tam być przechowywane dowolne obiekty. Okazało się, że nowa instancja obiektu nie jest tworzona dla każdego żądania http, tylko obiekt ten jest czyszczony na końcu, a następnie wykorzystywany jeszcze raz. Okazało się więc, że wątek odpowiadający za raportowanie wyjątków mógł pobrać env w momencie, gdy przetwarzane było jedno żądanie, a zapisać coś do env, gdy przetwarzane było już inne żądanie. Stąd nagle użytkownik otrzymywał ciasteczka należące do kogoś innego.

Dobrze obrazuje to poniższy diagram:

Źródło: https://github.blog/2021-03-18-how-we-found-and-fixed-a-rare-race-condition-in-our-session-handling/

Przeanalizujmy zatem co się dzieje:

  1. W pierwszym kroku wysyłane jest anonimowe żądanie http (nazwane na diagramie request #1). W tym momencie w wątku do zarządzania wyjątkami został utworzony kontekst, który m.in. zarejestrował callbacki odwołujące się do współdzielonego obiektu env.
  2. W tle wystąpił wyjątek. Wątek zarządzający wyjątkami skopiował więc obecny kontekst zapytania, żeby umieścić go w raporcie. Kontekst zawiera też callbacki utworzone w poprzednim kroku.
  3. Inny użytkownik wykonuje zapytanie do GitHuba (request #2 na diagramie).
  4. Wątek zarzadzający wyjątkami działający w tle zaczyna przetwarzać callbacki. Jednym z zadań callbacków jest pobranie identyfikatora sesji użytkownika. Ponieważ oryginalne zapytanie było anonimowe, żaden identyfikator nie został wcześniej pobrany. Dlatego zostaje wykonane żądanie do systemu uwierzytelniającego. Ten system z kolei pobiera numer sesji z obecnego obiektu env, który w tym momencie zawiera już dane z innego zapytania, niż to, w którym oryginalnie wystąpił wyjątek!
  5. Zapytanie #2 zostaje zakończone.
  6. Kolejne zapytanie (request #3) zostaje wyzwolone.
  7. W tle żądanie uwierzytelniające zostaje zakończone i nowe ciasteczko sesyjne zostaje zapisane do env. Ale w tym momencie env powiązane jest z zapytaniem #3!
  8. Użytkownik dostaje odpowiedź z zapytania #3, które – ze względu na wcześniejsze kroki – zostało uzupełnione o ciasteczko z zapytania #2. Użytkownik zostaje zatem zalogowany na konto innego użytkownika.

Błąd jest więc bardzo poważny, ale jak widać – musiały wystąpić specyficzne zapytania w specyficznej kolejności, by mógł wystąpić. Tak czy owak, ciekawa analiza ze strony GitHuba i brawa za otwartość!

Po więcej szczegółów odsyłamy do oficjalnej notki z blogu GitHuba.

— Michał Bentkowski

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

Odpowiedz