Pytanie:
Wykorzystanie opóźnienia w skanowaniu biletu festiwalowego
O'Niel
2019-07-29 16:18:55 UTC
view on stackexchange narkive permalink

Jak działa system biletowy

System biletowy - taki, który widzisz na festiwalach - działa w ten sposób: kiedy użytkownik płaci za bilet, do bazy danych dodawany jest wiersz z kolumną o nazwie is_scanned , którego domyślną wartością jest false.

Gdy tylko strażnik na festiwalu zeskanuje kod kreskowy (zawierający identyfikator i unikalny hash) za pomocą swojego urządzenia, wysyłane jest żądanie do bazy danych, aby sprawdzić, czy:

  1. użytkownik pasujący do identyfikatora i skrótu zapłacił, oraz
  2. czy wartość kolumny is_scanned jest nadal ustawiona na false.

Jeśli oba warunki są spełnione, ustawia wartość is_scanned na true , , aby zapobiec kopiowaniu biletu / kodu kreskowego przez inną osobę.

Problem z luką w zabezpieczeniach

Problem polega na tym, że czas między wysłaniem żądania przez urządzenie skanujące, a wartość is_scanned jest przełączana z false na true.

Rozważ tę scenę rio: Alice ma ważny bilet, za który zapłaciła, ale potem pozwala Ewie skopiować jej kod kreskowy i zmienia widoczne imię na fałszywym bilecie z Alice na Eve. Więc teraz mamy dwa bilety. Jeden ważny i jeden fałszywy, ale oba mają ten sam kod kreskowy, jedyną różnicą jest nazwa.

A co, jeśli bilet Alicji i Ewy zostanie zeskanowany dokładnie w tym samym czasie, gdy wchodzą na festiwal ? System zgłoszeń nie przełączył is_scanned na true na czas, aby upewnić się, że Ewa nie może wejść z tym samym kodem kreskowym co Alice. Powoduje to, że oba bilety (ważne i fałszywe) są pokazywane strażnikom jako „ważne”.

Możliwe rozwiązania

Oczywiście tego rodzaju exploit naprawdę zależy od wielu szczęście, i chociaż teoretycznie jest to możliwe ... w prawdziwym scenariuszu prawdopodobnie by się to nie udało.

Jak jednak możemy pokonać tego rodzaju exploit również w teorii?

Identyfikacja

Ten exploit już wziąłem pod uwagę, używając następującej metody: Podczas skanowania kodu kreskowego wyświetlam nie tylko, czy bilet jest ważny (spełnia podane wcześniej warunki), ale także nazwa w bazie danych. Jeśli nazwa nie zgadza się z nazwą na bilecie, wiemy, że bilet jest w jakiś sposób manipulowany. Ponadto, jeśli imię i nazwisko, które pojawia się na urządzeniu skanującym, nie zgadza się z imieniem w identyfikatorze (które i tak każdy musi pokazać, aby udowodnić wiek), wpis jest również zabroniony.

obejście tego rozwiązania jest oszustwem tożsamości i oczywiście wykracza poza odpowiedzialność systemu biletowego za sprawdzenie.

Opóźnienie

Innym sposobem rozwiązania tego teoretycznie jest dodanie losowy czas opóźnienia między każdym żądaniem skierowanym do bazy danych / API walidacji. W ten sposób nikt nie byłby w stanie zeskanować biletu w tym samym czasie ... ponieważ czas walidacji jest za każdym razem opóźniany o losową ilość milisekund.

Nie jestem tego fanem , ponieważ:

  1. spowalnia wszystko przy wejściu
  2. nie jest efektywne, jeśli nie jest wystarczająco opóźnione. Ponieważ jeśli aktualizacja bazy danych is_scanned z false do true zajmie 50 ms, jedynym rozwiązaniem byłoby opóźnienie jej o minimum 50 ms za każdym razem.

Inne rozwiązania?

Jakie inne rozwiązania myślisz o rozwiązaniu tego exploita?

Komentarze nie służą do rozszerzonej dyskusji;ta rozmowa została [przeniesiona do czatu] (https://chat.stackexchange.com/rooms/96932/discussion-on-question-by-oniel-exploiting-the-delay-when-a-festival-ticket-is).
Osiem odpowiedzi:
Benoit Esnard
2019-07-29 16:32:12 UTC
view on stackexchange narkive permalink

Luka, którą opisujesz, to stan rasy.

Jest kilka sposobów, aby sobie z tym poradzić, ale poleciłbym SELECT ... FOR UPDATE Zapytanie SQL, które blokuje wybrane wiersze, aby zapobiec nowym zapisom do czasu zatwierdzenia bieżącej transakcji.

Pamiętaj, aby sprawdzić dokumentację RDBMS, aby sprawdzić, jak poprawnie zaimplementować:

Wielkie dzięki!Miałem właśnie zapytać, czy istnieje sposób na kolejkowanie żądań skierowanych do tego samego wiersza w tym samym przedziale czasu.Ale to jest jeszcze lepsze.;)
Właściwie jak to rozwiązuje problem?Ponieważ w rzeczywistości powinieneś uniemożliwić odczyt zamiast zapisu.Ponieważ kiedy bilet Foo jest skanowany, wiersz X jest zablokowany do zapisu ... Ale kiedy bilet Bara zostanie zeskanowany, nadal może odczytać wiersz X i nadal będzie wskazywał, że jest ważny, ponieważ is_scanned jest fałszywe?Nie ma znaczenia, czy może aktualizować, czy nie?
Czy nie byłoby lepiej wykonać taką ręczną blokadę?https://stackoverflow.com/a/16119066/6533037?Niż mógłbym również zwrócić false, gdy ilość w kolumnie jest mniejsza niż wartość domyślna.A może SELECT ... FOR UPDATE jest dokładnie taki sam?
@O'Niel: zapytanie `SELECT ... FOR UPDATE` blokuje również inne zapytania` SELECT ... FOR UPDATE`!
Blokowanie jest zdecydowanie najłatwiejszym sposobem uniknięcia tego problemu.
Możesz również użyć optymistycznego blokowania, q.v.
@DavidConrad Właśnie sprawdziłem różnicę między optymistyczną i pesymistyczną kontrolą współbieżności.Jednak który z nich jest Twoim zdaniem najlepszy w tym przypadku?
@O'Niel W tym przypadku optymistycznie wydaje mi się bardziej odpowiedni, ponieważ szansa na faktyczną rywalizację jest bardzo, bardzo mała.
Język programowania, którego używasz dla swojego API, również _ prawdopodobnie_ oferuje Ci możliwość tworzenia blokad (blokowanie, odczyt, aktualizacja, odblokowanie).Zaletą jest to, że zachowujesz mechanizm blokujący nawet po zmianie warstwy danych.
Dobra odpowiedź na bezpośredni problem, ale istnieje wiele scenariuszy awarii.Co się stanie, jeśli bezprzewodowy skaner utraci połączenie w połowie?A jeśli ktoś przypadkowo zeskanuje bilet dwa razy?Co się stanie, jeśli osoba zostanie zeskanowana, ale nie może natychmiast wejść na teren z powodu jakiejś zatoru?Rzeczywisty problem jest taki: ** Masz dwie transakcje: jedną w bazie danych, a drugą stan świata rzeczywistego, jeśli dana osoba przeszła przez bramę, która musi zostać idealnie zsynchronizowana atomowo **, co po prostu nie jest łatwe.Będziesz więc musiał uwzględnić wiele procedur obsługi błędów
Dodanie asymetrycznej weryfikacji klucza HMAC podobnej do JWT do kodu QR może pomóc w zapobieganiu „przeróbce w Photoshopie” / fałszowaniu kodów QR poprzez podpisanie rzeczywistego ładunku.Rozproszona blokada wspierana przez Redis (lub podobny) może zapobiec zachowaniom przypominającym podwójne wydawanie bez spowalniania bazy danych.
@Tom: Nawet w przypadku festiwalu z 250 000 ludzi i 1000 skanerów bramek, pomyślałbym, że jedna stosunkowo skromna maszyna biurkowa byłaby w stanie pomieścić cały zestaw danych w pamięci RAM i przetwarzać wyszukiwania, indywidualnie, w mniej niż 0,1 milisekundy każdy.Jaka byłaby wada zwykła serializacja wszystkiego?
@Falco Twój komentarz jest doskonały.Rzeczywiście, czynniki ludzkie mogą złagodzić te zdarzenia.To znaczy.w końcu ** strażnik może cię wpuścić, jeśli ONI mają błąd **
Chociaż polecenie „SELECT ... FOR UPDATE” z pewnością złagodzi sytuację wyścigu, odbywa się to kosztem większej ilości zasobów (w bazie danych).To nic wielkiego, jeśli masz oddzielną bazę danych dla każdego wydarzenia.Ale wyobraź sobie, że masz setki lub tysiące wydarzeń odbywających się jednocześnie i tysiące uczestników na każdym wydarzeniu, wszyscy próbujący zeskanować swoje bilety w krótkim czasie.Musisz upewnić się, że masz zasoby do obsługi swojego przypadku użycia.W przypadku większości systemów RDBMS dodatkowe blokady wymagają więcej pamięci.Może niewiele, ale pomnożone przez setki tysięcy jednoczesnych transakcji się sumuje.
@ChristopherSchultz to w dużym stopniu zależy od implementacji bazy danych.Jeśli twoja baza danych obsługuje prawdziwe blokowanie na poziomie wiersza, nie powinno być problemu ze skalowaniem.Wiele baz danych obsługuje tylko blokowanie na poziomie bloków, w tym przypadku można to rozwiązać za pomocą inteligentnego grupowania lub partycjonowania odpowiedniej tabeli.I nie mogę tego wystarczająco podkreślić: bazy danych są tworzone dla milionów wierszy i tysięcy równoległych transakcji, o ile twoje wydarzenia nie obejmują odpowiedniego ułamka wszystkich ludzi na ziemi, powinno być dobrze ;-)
@ChristopherSchultz, jeśli ktoś „ma setki lub tysiące zdarzeń zachodzących jednocześnie”, to na pewno ma zasoby i ekspertów, aby sobie z tym poradzić, nie pytając tutaj, prawda?Do czasu, gdy firma rozrosła się do tak dużego rozmiaru, miała wystarczająco dużo czasu, aby nauczyć się skalowania.
@Mołot Retrospektywne planowanie wydajności zwykle nie daje zadowalających rezultatów.
@ChristopherSchultz dlaczego retrospektywa?Szukałem największych sprzedawców biletów w USA.Moje wyszukiwanie zwróciło Ticketmaster.W ten weekend sprzedają bilety na 1214 wydarzeń.Jeśli w jakiś sposób zdobędą pieniądze na setki razy większą ekspansję, dlaczego nie mogliby planować z wyprzedzeniem?A jeśli wzrost był powolny, dlaczego w międzyczasie nie mogli zmienić platformy?Mieliby na to kilkaset więcej pieniędzy niż mają teraz najwięksi sprzedawcy biletów, prawda?I to przy założeniu, że w dowolnym momencie zdecydują się nie rozdzielać serwerów na wydarzenie, miasto lub inny możliwy do zarządzania fragment obciążenia.
nvoigt
2019-07-29 17:56:18 UTC
view on stackexchange narkive permalink

Inne rozwiązanie tutaj jest absolutnie słuszne i ma sens w przypadku większych systemów, w których nie jest to takie łatwe.

Mając dane, które posiadasz, jest to stosunkowo proste, możesz skorzystać z opcji nieblokującej:

  UPDATE [FESTIVAL_TICKET] SET IS_SCANNED = TRUEWHERE TICKET_ID = @ScannedKey AND IS_SCANNED = FALSE  

To jest niepodzielna operacja. Żadne dwóch użytkowników bazy danych nie może wydać tego i kazać jej zaktualizować wiersza. Osoba, która otrzyma zwrot „1 wiersz dotknięty” (oczywiście istnieje sposób, aby to sprawdzić w kodzie, nie analizuj tego tekstu) może wejść. Wszyscy inni otrzymają zero wierszy, na które ma wpływ instrukcja. Jeśli chcesz być przyjazny dla użytkownika, możesz teraz sprawdzić, dlaczego nie można go znaleźć, czy był to zły identyfikator, czy jest już przeskanowany.

Ale ważny szczegół jest taki, że stwierdzenie jest niepodzielne. Tylko jeden wygra, bez względu na to, jak blisko jest zerowej różnicy czasu. Ponieważ nie masz już czytania i potem zapisu. Masz odczyt i zapis w jednej operacji atomowej.

Komentarze nie służą do rozszerzonej dyskusji;ta rozmowa została [przeniesiona do czatu] (https://chat.stackexchange.com/rooms/96933/discussion-on-answer-by-nvoigt-exploiting-the-delay-when-a-festival-ticket-is-sc).
J. Chris Compton
2019-07-31 03:07:34 UTC
view on stackexchange narkive permalink

Wadą tego wydaje się , że osoba może wejść za darmo (ze skopiowanym biletem).

W przypadku małych wydarzeń, które prawdopodobnie są poprawne.
Jednak jeśli dodasz zbyt duże opóźnienie z jakiegokolwiek powodu, ryzykujesz więcej niż wejście osoby .
skanery biletów po prostu przepuszczą kilka dodatkowych osób, jeśli ich urządzenia się zacinają lub są zbyt wolne ... ponieważ, do cholery, większość z nich prawdopodobnie ma ważne bilety, prawda?

Widziałem, jak to się dzieje podczas ważnego wydarzenia w tym roku kalendarzowym, w którym uczestniczyło tysiące fanów muzyków, o których wielu słyszało.
firma biletowa była główną firmą (może ta, dla której pracujesz?) i znajdowała się w miejscu, które zostało specjalnie zbudowane do przyjmowania biletów.
Moja grupa była jedną z tych, które przepuszczano bez skanowania (i tak ... Miałem ważny / legalny bilet).
Musiałem tam stać i obserwować przyjmujących bilety kilka minut, zanim mogłem się domyślić, dlaczego tak się stało.

TL; DR;
Nie koduj na każdą ewentualność kiedy zaangażowani są ludzie .
Strzelaj za dwie lub trzy dziewiątki (99% -99,9%) i nazwij to dniem.
Zachowaj wybór nicka, gdy w grę wchodzą tylko maszyny ... wtedy możesz zdobyć kilka dziewiątek.

Najgorszy scenariusz nie jest nawet taki, polega na tym, że twoi ochroniarze działają automatycznie i dziesiątki lub setki osób z legalnymi biletami zostają zablokowane.Twoja historia byłaby zupełnie inna, gdyby tak się stało, a nie tylko „przydarzyło mi się kiedyś coś śmiesznego”.
usr-local-ΕΨΗΕΛΩΝ
2019-07-30 18:58:37 UTC
view on stackexchange narkive permalink

Ta odpowiedź ma już świetną i tanią odpowiedź, jednak dodałbym własną odpowiedź zarówno z punktu widzenia inżynierii oprogramowania , jak i bezpieczeństwa. Może to pomóc w przyszłych podobnych pytaniach dotyczących mało prawdopodobnych exploitów .

i chociaż teoretycznie jest to możliwe ... W prawdziwym scenariuszu prawdopodobnie by się to nie udało.

A zatem, jakie są potencjalne szkody w porównaniu z kosztami? Mam zamiar udowodnić, że wysiłek i ból głowy na dodatkowe zabezpieczenia nie są warte ryzyka.

Teraz rozwiązanie już zaproponowane i zaakceptowane, aby poprawnie obsługiwać warunki wyścigu z transakcjami SQL, przenosząc odpowiedzialność / koszt na bazę danych , to najlepsze, zgodne ze standardami branżowymi i tańsze rozwiązanie. To może być koniec sprawy, ponieważ odpowiedź rzeczywiście została zaakceptowana”.

Jak już wspomniano, zdarzenie polegające na tym, że obaj uczestnicy są skanowani w bardzo dokładnym momencie i wyzwalają Exploit związany z sytuacją wyścigową można oszacować na milion, jeśli nie miliardy wielkości. Aby dać Ci jakościowy pogląd na szanse miliarderów, przeczytaj ten artykuł o loteriach i przekonaj się, że granie w SuperEnalotto na drugiej liście może być łatwą grą w porównaniu ze skanowaniem dwóch biletów, a nagroda jest zdecydowanie spójna. Szanse reprezentują możliwość wykorzystania luki, która jest zwykle klasyfikowana na dyskretnych poziomach ([bardzo] mało prawdopodobne, [bardzo] prawdopodobne ). Zawsze porównuję niedeterministyczne wydarzenia związane z bezpieczeństwem z loterią, aby zapewnić bardziej znane porównanie.

Aby dodatkowo wyjaśnić, na szanse wpływają:

  • Możliwość aby zsynchronizować ich przemieszczanie się w kolejkach i jednocześnie wręczać bilet strażnikowi. Oznacza to, że obaj mają ciągłą komunikację i trenują się, nie wspominając o szczęściu (szanse!), Że ich linie poruszają się z przewidywalną prędkością.
  • Fizyczny ruch strażników. Nie wszyscy strażnicy skanują bilety dokładnie w milisach, poruszają rękami z różnymi prędkościami. Jeden z biletów może spaść z rąk strażnika, jeden strażnik może odwrócić bilet. Jeden strażnik może się odwrócić, aby sprawdzić, czy linia nie jest zablokowana za nim. Innymi słowy, istnieje nadmierna entropia podczas planowania ataku
    • Bezzałogowe automaty do sprawdzania biletów mogą nie podlegać wpływowi tego czynnika
  • Czas potrzebny systemowi komputerowemu na zeskanowanie biletu, tak aby dwa skany wypadały w tym samym przedziale czasowym i luka została wykorzystana.

A więc tutaj jest kwestia inżynierii oprogramowania.

Bilet ma swoją cenę, więc jest wart X $. Szacuję, że wielkość X może być rzędu 50-100. Każda osoba wykorzystująca lukę i wchodząca do obiektu w sposób nieuczciwy wiąże się z utratą X $.

Wdrażanie bardziej złożonych kontroli (np. Kontrola nazwiska paszportu) jest kosztowne zarówno pod względem kod wymagany w fazie tworzenia oprogramowania i czas potrzebny ludziom na wejście do obiektu. Ochroniarze są opłacani co godzinę. Wdrożenie kontroli bezpieczeństwa w stylu Ben-Guriona * jest znacznie droższe i bardziej bolesne dla uczciwych.

Teraz chcesz spać lepiej, mając pewność, że nikt nie może wykorzystać Twojego systemu. Ile to kosztuje? Po zapłaceniu olbrzymiej sumy pieniędzy za zabezpieczenie systemu może się okazać, że Twój konkurent, prowadzący „niezabezpieczony” system, toleruje stratę 80 USD z prawdopodobieństwem ponad miliona wielkości. Trudno jest oszacować to prawdopodobieństwo. Ponieważ masz większe szanse na wygranie najtrudniejszej loterii na świecie, lepiej postaw na to, że na dobre odejdziesz z pracy!

Wniosek: w naszym zawodzie szanse na wygraną są naszym najlepszym śpiącym partnerem!

Wniosek 2: ataki na warunki wyścigu mogą być prawdopodobne wykorzystane w zautomatyzowanych systemach sieciowych, w których atakujący mogą oczywiście zsynchronizować się co do mikrosekundy !!! To może również zwielokrotnić szkody, więc witaj z zadowoleniem najlepsze środki bezpieczeństwa!

Wniosek 3: jeśli system już działa, wysiłek związany z łataniem go akceptowaną odpowiedzią (projekt , programowanie, testowanie, UAT, wdrożenie, PMO ...) jest droższe od potencjalnych szkód. Proszę o komentarz poniżej

* Podałem to jako przykład, ponieważ linie bezpieczeństwa na lotniskach w Izraelu są legendarnie długie i dokładne

To brzmi tak, jakby prawnik, który zmienił Project Managera, usprawiedliwiając nie wykonywanie swojej pracy w 100% ... Korzystając z przykładu w poście, ja również skorzystam z przykładu i podam, że koszt rozwiązania problemu jest mniejszy niżani grosza, ani jak długo zajmie programiście wpisanie kodu @nvoigt's.Jeśli zły aktor odkryje, że ta luka istnieje, ZNAJDZIE sposób, aby ją wykorzystać. Jeśli kolejka przyspiesza, niech osoba za nim odejdzie, przetasuj torbę, aby znaleźć bilet, podczas gdy druga osoba będzie mogła się zsynchronizować, iuwielbiał też „… może nie być dotknięty”, czyli wisienka na torcie
Naprawdę spodziewałem się komentarza takiego jak twój, sir, i cieszę się, że mogę to rozwinąć.Zamiast sugerować ** nie ** wykonywania pracy, zwykle sugeruję, aby umieścić tę pracę w zaległościach, ale oczywiście nadal to robię.To robi dużą różnicę.Jeśli nie masz idealnego dysku CD (Continuous Delivery) i samodzielnej aktualizacji w chmurze, wykonanie UAT i wdrożenie produktu wiąże się z kosztami.Zwłaszcza w przypadku automatów biletowych, wprowadzenie oprogramowania prawdopodobnie oznacza aktualizację oprogramowania sprzętowego przez kabel.Jeśli wprowadzasz dużo zmian, koszt jest mieszany ze wszystkimi zmianami.
Ponadto, jedną rzeczą, którą zwykle widzą kierownicy projektów (w porównaniu z programistami), jest to, że chociaż może się wydawać, że koszt wpisania jakiegoś kodu jest trywialny, zawsze istnieją koszty i ryzyko związane ze zmianami oprogramowania.Zajęłoby mi to bardzo dużo czasu na wyjaśnienie, ale uważam, że przytrafiła mi się następująca prawdziwa historia: aktualizacja Hibernate 3.5.x do 3.5.y po tym, jak skan Black Duck zgłosił lukę (ostatecznie niewykorzystaną) zepsuła aplikację produkcyjną, która nie została w pełni odtworzonaprzetestowany.Zmiana wersji Hibernacji zajęła mi 3 sekundy, a zespołowi zajęło kilka dni, aby poradzić sobie z katastrofą
Twoje komentarze zakładają, że jedyną aktualizacją byłaby obsługa luk w zabezpieczeniach, podczas gdy pytanie OP brzmi tak, jakby wciąż był w trakcie opracowywania. A w ostatnim komentarzu i poprawię, rozumiem, że zmieniłeś wersję oprogramowania w prod i zmieniłeś tylko wersję oprogramowania, a to zepsuło produkt?
Nie było dla mnie jasne, że oprogramowanie jest nadal w fazie rozwoju.Dobry czas, aby wprowadzić zmianę do zaległości.Tak, mówię, że zmieniłem wersję Hibernate tylko w projekcie frameworkowym, ponownie przetestowałem framework, poprosiłem innych programistów, aby wciągnęli nową wersję frameworka i jego Hibernację do swojego projektu, nie wykonałem pełnego ponownego testu i odkryłem, że HQLużywał niedozwolonej składni **, która była tolerowana ** we wcześniejszej wersji.Stało się to wiele lat temu, więc nie mam pod ręką pełnych szczegółów (i nie wiem, czy mogę się nimi podzielić, gdybym je miał)
Wniosek 3 nadal ma zastosowanie do IMO.** Jeśli ** Twój system działa i znalazłeś lukę w zabezpieczeniach, musisz ** najpierw ją ** segregować **, a nie ** spieszyć się ** z naprawą, jeśli stwierdzisz, że szanse i szkody są absurdalnie niskie.Luki w zabezpieczeniach o dużym wpływie / wykorzystaniu powinny * nadal * być załatane jak najszybciej, to jest poza dyskusją
Błąd stanu wyścigu może być poważniejszy w połączeniu z innym błędem.Na przykład.jeśli istnieje błąd, który znacznie skraca czas odpowiedzi systemu, to całkowicie nieprawdopodobne zdarzenie wystąpienia sytuacji wyścigu może zostać zmanipulowane tak, aby było bardzo, bardzo prawdopodobne.Zgodnie ze szwajcarskim modelem sera Jamesa Reasona: Nie czekaj, aż plasterki sera zrównają się i udowodnij, że „wysiłek związany z łataniem go [..] jest droższy niż potencjalne uszkodzenie” - założenie jest błędne: Napraw błąd!
Bez poparcia głównego punktu, Twój post prawdopodobnie lepiej zilustruje Twój punkt widzenia, porównując go z fizycznym bezpieczeństwem, takim jak możliwość przeskoczenia przez płot, wejścia przez okno lub wpuszczenia kumpla przez jakieś boczne drzwi.A może w porównaniu do innych zagrożeń, takich jak utrata połączenia przez strażników / obiekt z centralną bazą danych.Naprawianie błędów i zmniejszanie podatności jest dobre, ale wolałbym zobaczyć czas spędzony np.planowanie awaryjne na wypadek utraty połączenia sieciowego z centralną bazą danych, jeśli nie zostało to jeszcze zrobione.
Cyrbil
2019-07-31 22:06:57 UTC
view on stackexchange narkive permalink

Tutaj są już dobre odpowiedzi, które obejmowały wiele exploitów części bazy danych, ale chciałem dodać moje prawdziwe doświadczenie, pracując na polu imprezy (festiwalu na świeżym powietrzu) ​​i projektując weryfikację biletów system i aplikacje.

Jednym z największych wyzwań jest stabilność sieci, ponieważ założenie, że wszystkie urządzenia skanujące mają możliwość pracy w sieci przez cały czas, jest dość błędne. Mogą wystąpić opóźnienia, przerwy lub niedostępność w dowolnym momencie podczas skanowania i nie powinno to opóźniać wejścia klientów na wydarzenie (przynajmniej z naszego punktu widzenia, inne wydarzenia mogą wymagać ściślejszej walidacji).

W naszej aplikacji bilety były sprawdzane przy użyciu podpisu, ale były synchronizowane i zatwierdzane w bazie danych tylko wtedy, gdy sieć była aktywna. Aplikacja zapisała sprawdzone / przeznaczone do zatwierdzenia bilety w zasobniku i próbowała zatwierdzić jak najwięcej biletów, gdy sieć była dostępna. Unikaj również wykonywania jednego INSERT na bilet.

Najdalej przy wejściu na wydarzenie wifi w ogóle nie docierało. Aby zaoszczędzić na kosztach posiadania innego routera dla zaledwie 10 metrów większego zasięgu, urządzenia skanujące mogą komunikować się między nimi i udostępniać swoje połączenia. Oznacza to, że gdyby tylko jedno urządzenie miało dostęp do Wi-Fi, inne mogłyby teoretycznie wysłać do niego swoje wiadra lub do najbliższego urządzenia w zasięgu, które je przekaże.

Prawdziwe życie pokazało nam, że większość z nich urządzenia skanujące traciły połączenie przynajmniej raz na minutę.

Teoretycznie więc jeden bilet może zostać zeskanowany przez dowolną liczbę niepodłączonych urządzeń, ale tylko raz na urządzenie. To jest stan rasy, ale o wiele bardziej trywialne wykorzystanie niż wspomniane inne odpowiedzi.


Słowo na temat Jak temu zapobiec? :

Możesz zapobiec wyścigowi, usuwając równoległość, co robi blokada. Wprowadzi opóźnienia, ponieważ zasadniczo ograniczasz zdolność bazy danych do akceptowania współbieżnych zapisów.

Pytanie brzmi, czy warto? Czy możemy zaakceptować dłuższe opóźnienie do weryfikacji, aby zapewnić prawidłowe uwierzytelnienie? Czy to uniemożliwi ludziom znalezienie otwartych drzwi lub przeskoczenie przez płot?

+1 za prawdziwe doświadczenie życiowe
+1 za pomysł na sieć kratową / protokół plotek, jeśli musisz polegać na słabym połączeniu sieciowym
paolord
2019-07-31 08:06:54 UTC
view on stackexchange narkive permalink

Dobrym rozwiązaniem może być użycie kolejki komunikatów zamiast wprowadzania opóźnień. Zeskanowany bilet nie jest przetwarzany od razu, czeka, aż wszystkie wysłane bilety zostaną przetworzone. System nie zwraca odpowiedzi, chyba że bilet opuścił kolejkę i został poprawnie przetworzony. Argumentem byłoby to, że mogłoby to być powolne, ponieważ każdy musi czekać, aż inni skończą. ale możesz logicznie podzielić identyfikatory biletów na fragmenty, na przykład: 2 kolejki, 1 dla biletów nieparzystych i 1 dla biletów parzystych. lub po prostu umieść numery grup w samym identyfikatorze.

W przypadku wydarzenia z 1 000 000 biletów nawet skromny komputer stacjonarny mógłby uruchomić program, który tworzyłby listę zeskanowanych biletów w pamięci i sprawdzałby ich duplikaty z szybkością ponad miliona biletów na sekundę.Nawet dość uproszczony program Javascript w przeglądarce internetowej może zbliżyć się do tej prędkości (właśnie wygenerowałem milion losowych biletów w mojej przeglądarce i naliczyłem 999 870 unikalnych w mniej niż 1,2 sekundy).Nawet gdyby istniało dziesięć tysięcy bramek z ludźmi skanującymi bilety, najgorsze opóźnienie wyniósłby ułamek sekundy.
@supercat Bramy muszą się ze sobą komunikować.Bilety nie są zwykle rezerwowane na daną bramę.Twój uproszczony program javascript pozwoliłby na przyjęcie dziesięciu tysięcy kopii tego samego biletu.Jak zwykle to nie procesor jest czynnikiem ograniczającym - to IO.Pamiętaj, że systemy biletowe, nad którymi pracowałem, musiały być tolerancyjne na awarie połączeń, więc zawsze musiały popełniać błędy „raczej wpuszczać ludzi bez biletów, niż je uniemożliwiać”.
@Luaan: Nie, mówię, że wszystkie lejki do bram wysyłają żądania przez jeden komputer.Jeśli komputerowi zajmuje mniej niż dwie mikrosekundy, aby przetworzyć każde żądanie (poziom wydajności łatwo osiągalny w prostym JavaScript w przeglądarce) lub nawet jeśli zajmuje to dziesięć razy tyle, to nawet jeśli żądania zostały odebrane ze wszystkich 10000 bram naraz, obsługawszystkie te żądania zajęłyby mniej niż jedną czwartą sekundy.
cjs
2019-07-31 09:44:38 UTC
view on stackexchange narkive permalink

Jest to typowy problem spowodowany niedopasowaniem między projektem Twojego systemu księgowego („księgowość” oznacza tutaj śledzenie wykorzystania dowolnego zasobu lub pozycji, nie tylko pieniędzy), a rzeczywistymi informacjami, które chcesz śledzić. p>

Wyjaśniłeś, że w prawdziwym świecie możliwe jest dwukrotne zeskanowanie kodu i chcesz śledzić tę sytuację, ale Twoja baza danych przechowuje tylko informację „została przeskanowana”. To nie jest błędne, jeśli chodzi o to, co się dzieje: SET is_scanned = TRUE WHERE ticket_id =? będzie rzeczywiście poprawne (jeśli w świecie rzeczywistym stan „ticket_id?” Został przeskanowany wcześniej, to jest nadal prawdziwe), ale to nie jest to, co chcesz rejestrować.

Zamiast utrzymywać stan, prowadź księgę: tabelę skanuj z nagłówkiem ticket_id, scanned_timestamp , scanner_id , gdzie kluczem są wszystkie trzy z tych pól.¹ W ten sposób rejestrujesz nie tylko to, że bilet został zeskanowany, ale każdy skan bilet. Jeśli nie ma wierszy zawierających ticket_id =? , wiesz, że bilet nigdy nie został zeskanowany, w przeciwnym razie masz listę wszystkich razy, kiedy został zeskanowany (i gdzie).

Teraz, gdy Twoja baza danych jest w stanie lepiej modelować informacje ze świata rzeczywistego, możesz zdecydować, co chcesz z tym zrobić. Twój konkretny DBMS może zaoferować jakiś sposób na obejście tego warunku wyścigu ( INSERT identyfikator_ biletu, skaner_id INTO skanuje GDZIE bilet_id NIE W skanach lub cokolwiek²). Alternatywnie możesz po prostu wpuścić obie osoby, a następnie przeanalizować swoje rekordy, aby dowiedzieć się, dlaczego wydawało się, że masz dwa bilety z tym samym identyfikatorem biletu.

To, którą drogą chcesz iść, jest w rzeczywistości decyzją biznesową, a nie techniczną. Istnieje wiele powodów, dla których możesz nieprawidłowo wystawić dwa ważne bilety z tym samym identyfikatorem biletu (powiedzmy, błąd związany z próbami zwrotu) lub bilet musi zostać użyty dwukrotnie przez tę samą osobę (zapominając o skanowaniu przy wyjściu). Jeśli liczba osób próbujących oszukać system jest niższa niż określony poziom, może być lepiej dla Twojej firmy, aby zjeść cenę kilku oszustów, niż zapłacić koszt (w reputacji, procesach sądowych lub w inny sposób) odmowy posiadania ważnego biletu .

Krótko mówiąc: dopasuj projekt bazy danych do rzeczywistej sytuacji, używając rejestrów zamiast stanów, w których ma to zastosowanie, i pozwól firmie podejmować decyzje biznesowe dotyczące radzenia sobie z rzeczywistymi sytuacjami.


¹ Macham rękami nad wieloma szczegółami tutaj, aby odpowiedź była krótka i wyraziła tylko ogólny pomysł.
² Zakładam tutaj odpowiednią strategię spójności, która ma ten sam efekt podczas serializacji tych żądań INSERT do tej tabeli.

OP nie prosi o „śledzenie” tego problemu, ale o zapobieżenie mu.Masz „machnął ręką” nad mechanizmem, aby zapobiec sytuacji wyścigu, co byłoby odpowiedzią.Twój nowy pomysł na stół ma ten sam problem z warunkami wyścigu.
Ta strategia `INSERT` jest również podatna na sytuacje wyścigu, jednak dodanie unikalnego klucza do` scans (ticket_id) `powinno wystarczyć, aby to naprawić.
@BenoitEsnard Musisz _ absolutnie nie_ dodawać unikalnego klucza do `skanów (identyfikator_ biletu)`;to jest cały punkt mojej odpowiedzi.I nie, nie ma warunków wyścigu, jeśli użyjesz odpowiedniego modelu spójności.Zaktualizowałem odpowiedź, aby spróbować wyjaśnić oba.
@schroeder Chodzi mi o to, że pytanie dotyczy tylko pewnych warunków biznesowych, a plakat powinien jasno określać, że rozważył i wyraźnie podjął tę decyzję biznesową przed rozwiązaniem tego problemu, w przeciwnym razie nie istnieje.Zazwyczaj większość osób, które zadają to pytanie, nie zrobiła tego.
Zgadzam się z innymi komentatorami, że ta strategia wciąż ma stan wyścigowy;w odpowiedzi nie omówiono „właściwego modelu spójności”, który by temu zapobiegał, poza pojedynczym zdaniem rozpoczynającym się od „Twój konkretny DBMS może ...”.Jeśli oboje zapobiegacie _i_ rejestrowaniu, możesz użyć jednej z pozostałych odpowiedzi i utworzyć dziennik „prób skanowania”, który można później przeanalizować, aby zobaczyć, ile osób zostało odrzuconych.Tak więc szczegóły techniczne w tej odpowiedzi są trochę rozpraszające: w rzeczywistości jest to _ wyzwanie ramkowe_ pytania, pytając, czy _dowolne_ rozwiązanie jest rzeczywiście konieczne.
Džuris
2019-07-30 02:06:55 UTC
view on stackexchange narkive permalink

W Clojure mają funkcję zwaną atomami, która zajmuje się takimi problemami.

Wchodzą dwa wątki. Oba odczytują wartość is_scanned = false i kontynuują. Po przetworzeniu swoich rzeczy zwracają is_scanned = true , aby zaktualizować atom. Jednak jeden wątek przyjdzie o chwilę szybciej.

Wątek, który pojawi się trochę później, będzie miał zmieniony atom, więc funkcja nie będzie w stanie zaktualizować atomu. Zamiast tego zostanie poproszony o ponowne uruchomienie z zaktualizowaną już wartością is_scanned = true . A teraz wynik będzie taki, że drugi bilet nie jest już ważny.

Tutaj jest film wyjaśniający tę funkcję clojure.

Nie jestem do końca pewien, jak to pomaga osobie zadającej to pytanie konkretnie, ponieważ stanowczo sugerowali, że używają relacyjnej bazy danych i chcą tam umieścić swoje rozwiązanie ... Nie widzę też, jak pomaga to programistom w tworzeniusystem jest bezpieczniejszy, ponieważ warunki wyścigu nie są wyłączne dla Clojure (ani żadnego języka), a Clojure nie ma monopolu na sposób radzenia sobie z warunkami wyścigu.
Ponadto, nawet jeśli twoje oprogramowanie jest napisane w Clojure, co zrobić, jeśli masz tak wiele żądań, że zdecydujesz się skalować aplikację w poziomie, uruchamiając wiele węzłów?Żadne rozwiązanie w pamięci nie zadziała;musisz go rozwiązać w bazie danych.


To pytanie i odpowiedź zostało automatycznie przetłumaczone z języka angielskiego.Oryginalna treść jest dostępna na stackexchange, za co dziękujemy za licencję cc by-sa 4.0, w ramach której jest rozpowszechniana.
Loading...