Domanda:
Sfruttare il ritardo durante la scansione del biglietto del festival
O'Niel
2019-07-29 16:18:55 UTC
view on stackexchange narkive permalink

Come funziona un sistema di ticket

Un sistema di ticket - quello che vedi ai festival - funziona così: quando un utente paga per il suo biglietto, una riga viene aggiunta al database con una colonna chiamata is_scanned , il cui valore predefinito è impostato su false.

Non appena una guardia al festival scansiona il codice a barre (contenente un ID e un hash univoco) con il proprio dispositivo, viene inviata una richiesta al database per verificare se:

  1. l'utente che corrisponde all'ID e all'hash ha pagato e
  2. se il valore della colonna is_scanned è ancora impostato su false.

Se entrambe le condizioni sono soddisfatte, imposta il valore is_scanned su true , per impedire a qualcun altro di copiare il ticket / codice a barre di entrare.

Il problema di vulnerabilità

Il problema qui è il tempo che intercorre tra la richiesta inviata dal dispositivo di scansione e il valore is_scanned viene modificato da false a true.

Considera questa scena rio: Alice ha un biglietto valido che ha pagato, ma poi lascia che Eve copi il suo codice a barre e cambia il nome visibile sul biglietto falso da Alice ad Eve. Quindi ora abbiamo due biglietti. Uno valido e uno fraudolento, ma entrambi hanno lo stesso codice a barre, l'unica differenza è il nome.

E se il biglietto di Alice ed Eve fosse scansionato esattamente nello stesso momento in cui entrano al festival ? Il sistema di ticket non commutava is_scanned a true in tempo per assicurarsi che Eve non potesse entrare con lo stesso codice a barre di Alice. Ciò fa sì che entrambi i ticket (quello valido e quello fraudolento) vengano mostrati come "validi" alle guardie.

Possibili soluzioni

Naturalmente, questo tipo di exploit dipende davvero da molti fortuna, e sebbene sia possibile in teoria ... in uno scenario reale, questo probabilmente fallirebbe.

Tuttavia, come possiamo sconfiggere questo tipo di exploit anche in teoria?

Identificazione

Ho già preso in considerazione questo exploit utilizzando il seguente metodo: Quando un codice a barre viene scansionato, visualizzo non solo se il biglietto è valido (soddisfa le condizioni indicate in precedenza), ma anche il nome nel database. Se il nome non corrisponde a quello sul biglietto, sappiamo che il biglietto è manipolato in qualche modo. Inoltre, se il nome che compare sul dispositivo di scansione, non corrisponde al nome sull'ID (che tutti devono comunque mostrare per provare l'età), anche l'immissione non è consentita.

L'unico modo per aggirare questa soluzione è la frode di identità e, ovviamente, il controllo va oltre la responsabilità del sistema di ticket.

Ritardo

Un altro modo per risolvere questo problema, in teoria, è aggiungere un tempo casuale di ritardo tra ogni richiesta effettuata al database / API di convalida. In questo modo, nessuno sarebbe in grado di scansionare il proprio biglietto contemporaneamente ... perché il tempo di convalida viene ritardato ogni volta con una quantità casuale di millisecondi.

Non sono un fan di questo , perché:

  1. rende tutto più lento all'ingresso
  2. non è efficace se non è sufficientemente ritardato. Perché se il database impiega 50 ms per aggiornare is_scanned da false a true , l'unica soluzione sarebbe ritardarlo con un intervallo minimo 50 ms ogni volta.

Altre soluzioni?

Quali altre soluzioni pensi per risolvere questo exploit?

I commenti non sono per discussioni estese;questa conversazione è stata [spostata in chat] (https://chat.stackexchange.com/rooms/96932/discussion-on-question-by-oniel-exploiting-the-delay-when-a-festival-ticket-is).
Otto risposte:
Benoit Esnard
2019-07-29 16:32:12 UTC
view on stackexchange narkive permalink

La vulnerabilità che stai descrivendo è una condizione di competizione.

Ci sono diversi modi per affrontarla, ma preferirei un SELECT ... FOR UPDATE Query SQL, che pone un blocco sulle righe selezionate per impedire nuove scritture fino a quando non viene eseguita la transazione corrente.

Assicurati di controllare la documentazione RDBMS per verificare come implementarla correttamente:

Molte grazie!Stavo per chiedere se c'era un modo per mettere in coda le richieste fatte alla stessa riga nello stesso lasso di tempo.Ma questo è anche meglio.;)
In realtà come risolve il problema?Perché in realtà dovresti impedire la lettura anziché la scrittura.Perché quando il biglietto di Foo viene scansionato, la riga X è bloccata per la scrittura ... Ma quando il biglietto di Bar viene scansionato, può ancora leggere la riga X e continuerà a dire valido perché is_scanned è falso?Non importa se può aggiornare o no?
Non sarebbe meglio fare un blocco manuale come questo?https://stackoverflow.com/a/16119066/6533037?Potrei anche restituire false quando la quantità della colonna è inferiore al valore predefinito.O SELECT ... FOR UPDATE è esattamente lo stesso?
@O'Niel: una query `SELECT ... FOR UPDATE` bloccherà anche altre query` SELECT ... FOR UPDATE`!
Il blocco è sicuramente il modo più semplice per prevenire questo problema.
Puoi anche utilizzare il blocco ottimistico, q.v.
@DavidConrad Ho appena cercato la differenza tra controllo della concorrenza ottimistico e pessimistico.Tuttavia, quale pensi sia il più adatto in questo caso?
@O'Niel In questo caso, ritengo che l'ottimista sia più adatto, poiché la possibilità di una vera contesa è molto, molto bassa.
Il linguaggio di programmazione che usi per la tua API _likely_ ti offre anche la possibilità di creare blocchi (Lock, Read, Update, Unlock).I professionisti ti stanno mantenendo il tuo meccanismo di blocco anche se hai cambiato il tuo livello dati.
Buona risposta per il problema immediato, ma ci sono molti scenari di fallimento seguenti.Cosa succede se lo scanner wireless perde la connessione a metà?Cosa succede se una persona esegue la scansione del biglietto due volte per sbaglio?Cosa succede se una persona viene scansionata, ma non può entrare immediatamente nel terreno a causa di qualche rapina?Il vero problema è: ** Hai due transazioni: una nel database e l'altra nello stato del mondo reale se la persona è entrata attraverso il cancello, che devono essere sincronizzate idealmente in modo atomico ** il che non è facilmente possibile.Quindi dovrai includere molta gestione degli errori
L'aggiunta di una verifica della chiave asimmetrica HMAC simile a JWT al codice QR può aiutare a prevenire codici QR "photoshoppati" / falsificati firmando il carico effettivo.Un blocco con backup Redis (o simile) distribuito potrebbe impedire comportamenti simili alla doppia spesa senza rallentare il database.
@Tom: Anche per un festival con 250.000 persone e 1.000 gate scanner, penserei che una singola macchina desktop relativamente modesta sarebbe in grado di contenere l'intero set di dati nella RAM ed elaborare le ricerche, individualmente, in meno di 0,1 millisecondi ciascuna.Quale svantaggio ci sarebbe a serializzare tutto semplicemente?
@Falco Il tuo commento è eccellente.Infatti, i fattori umani possono mitigare questi eventi.Cioèalla fine ** la guardia potrebbe lasciarti entrare se LORO hanno un difetto **
Sebbene `SELEZIONA ... PER AGGIORNAMENTO` certamente mitigherà la condizione di gara, ha il costo di più risorse (all'interno del database).Non è un grosso problema se hai un database separato per ogni evento.Ma immagina di avere centinaia o migliaia di eventi che si verificano contemporaneamente e migliaia di partecipanti a ogni evento che cercano tutti di scansionare i propri biglietti in un breve lasso di tempo.Devi assicurarti di avere le risorse per supportare il tuo caso d'uso.Per la maggior parte degli RDBMS, blocchi aggiuntivi richiedono più memoria.Forse non molto, ma moltiplicato per centinaia di migliaia di transazioni simultanee si somma.
@ChristopherSchultz dipende molto dall'implementazione del database.Se il tuo DB supporta un vero blocco a livello di riga, non dovrebbero esserci problemi di ridimensionamento.Molti DB supportano solo il blocco a livello di blocco, in questo caso può essere risolto con il bucket intelligente o il partizionamento della tabella pertinente.E non lo sottolineerò mai abbastanza: i DB sono fatti per milioni di righe e migliaia di transazioni simultanee, a patto che i tuoi eventi non includano una frazione rilevante di tutti gli esseri umani sulla terra dovresti stare bene ;-)
@ChristopherSchultz se qualcuno "ha centinaia o migliaia di eventi che si verificano contemporaneamente", sicuramente ha risorse ed esperti per affrontarlo senza nemmeno chiedere qui, giusto?Quando l'azienda è diventata così grande, ha ottenuto tutto il tempo necessario per imparare a scalare.
@Mołot La pianificazione della capacità retrospettiva di solito non produce risultati soddisfacenti.
@ChristopherSchultz perché retrospettiva?Ho cercato i più grandi venditori di biglietti negli Stati Uniti.La mia ricerca ha restituito Ticketmaster.Vendono biglietti per 1214 eventi questo fine settimana.Se in qualche modo avessero denaro per espandersi centinaia di volte di più, perché non potevano pianificare in anticipo?E se la crescita è stata lenta, perché nel frattempo non potevano cambiare piattaforma?Avrebbero un paio di centinaia di soldi in più per fare questo rispetto ai più grandi venditori di biglietti ora, giusto?E questo presumendo che in qualsiasi momento decidano di non separare i server per evento, per città o per altra parte gestibile del carico di lavoro.
nvoigt
2019-07-29 17:56:18 UTC
view on stackexchange narkive permalink

L'altra soluzione qui è assolutamente giusta e ha senso per sistemi più grandi in cui non è così facile.

Con i dati che hai, che è relativamente semplice, potresti optare per un'opzione non bloccante:

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

Ora, questa è un'operazione atomica. Nessun due utenti del database possono emettere questo e fare in modo che aggiorni la riga. La persona che viene restituita "1 riga interessata" (ovviamente c'è un modo per scoprirlo nel codice, non analizzare il testo per questo) può entrare. Tutti gli altri avranno zero righe interessate dall'istruzione. Se vuoi essere facile da usare, ora puoi verificare perché non è stato trovato, se era l'ID sbagliato o se è già scansionato.

Ma il dettaglio importante è che l'affermazione è atomica. Solo uno vincerà, non importa quanto siano vicini a zero differenza di tempo. Perché non hai più una lettura e poi una scrittura. Hai la lettura e la scrittura in un'unica operazione atomica.

I commenti non sono per discussioni estese;questa conversazione è stata [spostata in chat] (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

Lo svantaggio di questo sembra essere che una persona potrebbe entrare gratuitamente (con un ticket copiato).

Per piccoli eventi probabilmente è corretto.
Tuttavia, se aggiungi troppo ritardo, per qualsiasi motivo, rischi più di una persona di entrare.
gli scanner di biglietti lasceranno passare alcune persone in più se i loro dispositivi si inceppano o sono troppo lenti ... perché, diamine la maggior parte di loro probabilmente ha biglietti validi, giusto?

L'ho visto accadere durante un importante evento di quest'anno solare a cui hanno partecipato migliaia di fan di musicisti di cui molte persone hanno sentito parlare.
La compagnia di biglietti era una delle principali (forse quella per cui lavori?) ed era in un sito creato su misura per la raccolta dei biglietti.
La mia festa è stata una di quelle che sono state lasciate passare senza una scansione (e sì ... Avevo un biglietto valido / legale).
Dovevo stare lì a guardare gli acquirenti del biglietto alcuni minuti prima di poter capire perché era successo.

TL; DR;
Non codificare per ogni evenienza quando le persone sono coinvolte .
Scatta per due o tre nove (99% -99,9%) e chiamalo un giorno.
Salva la scelta dei nick per quando sono coinvolte solo le macchine ... è allora che puoi ottenere un mucchio di 9.

Lo scenario peggiore non è nemmeno quello, è che le tue guardie di sicurezza agiscono in modo robotico e decine o centinaia di persone con biglietti legittimi vengono bloccate.La tua storia sarebbe molto diversa se fosse successo, non solo "una cosa divertente mi è successa una volta".
usr-local-ΕΨΗΕΛΩΝ
2019-07-30 18:58:37 UTC
view on stackexchange narkive permalink

Questa risposta ha già un'ottima ed economica risposta, tuttavia, aggiungerei la mia risposta sia dal punto di vista dell ' ingegneria del software che dal punto di vista della sicurezza. Questo può essere utile per future domande simili su improbabili exploit .

e sebbene sia possibile in teoria ... In uno scenario reale, probabilmente non funzionerebbe.

E poi, qual è il potenziale danno rispetto al costo? Dimostrerò che spendere sforzi e grattacapi per una sicurezza aggiuntiva non vale il rischio.

Ora, la soluzione già proposta e accettata per gestire correttamente le condizioni di gara con le transazioni SQL, spostando la responsabilità / costo sul database , è la soluzione migliore, standard del settore ed economica. Potrebbe essere la fine del caso, poiché la risposta è stata effettivamente accettata”.

Come già sottolineato, il fatto che entrambi gli assistenti vengano scansionati nel momento esatto e attivino il l'exploit della condizione di razza potrebbe essere stimato in probabilità di milioni se non di miliardi di grandezza. Per darti un'idea qualitativa delle probabilità dei miliardari, leggi questo articolo sulle lotterie e scopri che giocare per quel SuperEnalotto in cima alla seconda lista potrebbe essere un gioco facile rispetto alla scansione di due biglietti e la ricompensa è decisamente coerente. Le probabilità rappresentano l ' sfruttamento di una vulnerabilità e che normalmente è qualificata in livelli discreti ([molto] improbabile, [molto] probabile ). Confronto sempre gli eventi non deterministici relativi alla sicurezza con la lotteria per fornire un confronto più familiare.

Per fare ulteriori chiarimenti, le probabilità sono influenzate da:

  • La capacità di i due sincronizzano il loro spostamento tra le code e allo stesso tempo consegnano il biglietto alla guardia. Ciò implica che i due abbiano una comunicazione costante e si siano allenati, per non parlare della fortuna (probabilità!) Che le loro linee si muovano a velocità prevedibili
  • Il movimento fisico delle guardie. Non tutte le guardie impiegano il tempo esatto in millisecondi per scansionare i biglietti, muovono le braccia a velocità diverse. Uno dei biglietti potrebbe cadere dalle mani della guardia, una guardia potrebbe trattenerlo al contrario. Una guardia può girarsi per controllare se la linea non è bloccata dietro di loro. In altre parole, c'è un'entropia eccessiva per pianificare un attacco
    • Le macchine di controllo dei biglietti senza equipaggio potrebbero non essere influenzate da questo fattore
  • Il tempo necessario al sistema informatico per eseguire la scansione del biglietto, in modo che le due scansioni rientrino nella stessa fascia oraria e la vulnerabilità venga sfruttata.

Quindi ecco la considerazione dell'ingegneria del software.

Il biglietto ha un prezzo, quindi vale X $. Stimo che la grandezza di X possa essere nell'ordine di 50-100. Per ogni persona che sfrutta la vulnerabilità e accede alla struttura in modo fraudolento, si applica una perdita di $ X.

Implementare controlli più complessi (ad es. Controllo del nome del passaporto) è costoso entrambi in termini di il codice richiesto nella fase di sviluppo del software e il tempo impiegato dalle persone per entrare nella struttura. Le guardie di sicurezza vengono pagate ogni ora. Implementare un controllo della sicurezza in stile Ben-Gurion * è molto più costoso e doloroso per gli onesti.

Ora, vuoi dormire meglio assicurato che nessuno possa sfruttare il tuo sistema. Quanto costa? Dopo aver pagato una somma di denaro esorbitante per proteggere il tuo sistema potresti scoprire che il tuo concorrente, che gestisce un sistema "non protetto", sta tollerando una perdita di $ 80 con probabilità di uno su milioni di grandezza. È difficile quantificare questa probabilità. Dato che hai più probabilità di vincere alla lotteria più difficile del mondo, è meglio che scommetti per lasciare il tuo lavoro per sempre!

Conclusione: nella nostra professione, le probabilità di vincita sono il nostro miglior partner addormentato!

Conclusione 2: gli attacchi in condizioni di competizione possono essere probabili sfruttati su sistemi di rete automatizzati dove gli aggressori possono ovviamente sincronizzarsi al microsecondo !!! Ciò potrebbe anche moltiplicare il danno, quindi benvenute le migliori misure di sicurezza!

Conclusione 3: se il sistema è già in esecuzione, lo sforzo di patchare con la risposta accettata , sviluppo, test, UAT, rollout, PMO ...) è più costoso del potenziale danno. Si prega di commentare di seguito

* L'ho citato come esempio perché le linee di sicurezza degli aeroporti in Israele sono leggendariamente lunghe e complete

Sembra che un avvocato sia diventato Project Manager giustificando il non fare il lavoro al 100% ... Dato che stai usando l'esempio nel post, anch'io userò l'esempio e ti dico che il costo per risolvere il problema è inferiore a uncentesimo o quanto tempo impiega il tuo sviluppatore a digitare il codice @nvoigt's.Se un cattivo attore scopre che esiste questa vulnerabilità, troverà un modo per sfruttarla.Lascia che la persona dietro di loro vada se la loro coda sta andando più veloce, mescola nella borsa per trovare il biglietto mentre l'altra persona si mette in posizione per la sincronizzazioneanche amato "... potrebbe non essere influenzato" che era la ciliegia inthispost
Mi aspettavo davvero un commento come il suo, signore, e sono felice di ampliare.Piuttosto che suggerire ** non ** di fare il lavoro, normalmente suggerisco di inserire quel lavoro nel backlog, ma ovviamente fallo ancora.Questo fa molta differenza.A meno che tu non abbia un CD (Continuous Delivery) perfetto e un aggiornamento automatico del cloud, c'è un costo per eseguire l'UAT e l'implementazione del prodotto.Soprattutto per le biglietterie automatiche, distribuire il software probabilmente significa aggiornare il firmware via cavo.Se spingi un sacco di cambiamenti, il costo è mescolato a tutti i cambiamenti.
Inoltre, una cosa che i project manager (rispetto ai programmatori) normalmente vedono è che, sebbene possa sembrare che il costo della digitazione di un codice sia banale, ci sono sempre costi e rischi associati alle modifiche al software.Mi ci vorrebbe molto tempo per spiegare, ma considera la seguente storia vera che mi è successa: l'aggiornamento di Hibernate 3.5.xa 3.5.y dopo che una scansione di Black Duck ha segnalato una vulnerabilità (alla fine non sfruttabile) ha rotto un'applicazione di produzione che non è stata completamente riutilizzata.testato.Mi ci sono voluti 3 secondi per cambiare le versioni di Hibernate e giorni al team per gestire il disastro
I tuoi commenti presumono che l'unico aggiornamento sia la gestione delle vulnerabilità, mentre la domanda dell'OP si legge come se fosse ancora nel mezzo dello sviluppo. E nell'ultimo commento e correggo di capire che hai cambiato la versione del software in prod e hai cambiato solo la versione del software, e che ha rotto il prod?
Non mi era chiaro che il software fosse ancora in fase di sviluppo.Buon momento per spingere il cambiamento al backlog.Sì, sto dicendo che ho solo cambiato la versione di Hibernate in un progetto framework, ho ri-testato il framework, ho chiesto ad altri sviluppatori di inserire la nuova versione del framework e il suo Hibernate nel loro progetto, non ho eseguito un nuovo test completo e ho scoperto che un HQLutilizzava una sintassi illegale ** che era tollerata ** dalla versione precedente.È successo molti anni fa, quindi non ho tutti i dettagli a portata di mano (e non so se posso condividere se li avessi)
La conclusione 3 si applica ancora IMO.** Se ** il tuo sistema è in esecuzione e hai trovato una vulnerabilità, devi prima ** valutarla ** e non ** affrettarti ** a risolverla se stabilisci che le probabilità e il danno sono ridicolmente bassi.Le vulnerabilità ad alto impatto / sfruttabilità dovrebbero * ancora * essere corrette al più presto, questo è fuori discussione
Il bug della race condition potrebbe essere più grave se combinato con un bug diverso.Per esempio.se c'è un bug che riduce drasticamente il tempo di risposta del sistema, allora l'evento completamente improbabile che si verifichi una condizione di competizione potrebbe essere manipolato in modo molto, molto probabile.Secondo il modello di formaggio svizzero di James Reason: non aspettare che le fette di formaggio si allineano e dimostra che il tuo "sforzo di rattopparlo [..] è più costoso del potenziale danno" - ipotesi sbagliata: correggi l'errore!
Senza approvare il punto principale, il tuo post probabilmente illustrerebbe meglio il tuo punto con un confronto con la sicurezza fisica, come la possibilità che qualcuno salti una recinzione, si arrampichi da una finestra o lasci entrare il proprio amico da qualche porta laterale.O forse con un confronto con altri rischi, come le guardie / il luogo che perdono la connessione a un database centrale.Risolvere i bug e ridurre le vulnerabilità è positivo, ma preferirei vedere il tempo speso ad es.pianificazione di emergenza per la perdita della connessione di rete a un database centrale, se non è già stato fatto.
Cyrbil
2019-07-31 22:06:57 UTC
view on stackexchange narkive permalink

Ci sono già buone risposte qui che hanno coperto gran parte degli exploit della parte del database, ma volevo aggiungere la mia esperienza di vita reale , avendo lavorato nel campo degli eventi (festival all'aperto) e progettando la convalida dei biglietti sistema e applicazioni.

Una delle grandi sfide è la stabilità della rete, perché il presupposto che tutti i dispositivi di scansione abbiano sempre funzionalità di rete è sbagliato. Possono verificarsi ritardi, interruzioni o indisponibilità in qualsiasi momento durante la scansione processo e questo non dovrebbe ritardare le iscrizioni dei clienti all'evento (almeno dal nostro punto di vista, altri eventi potrebbero richiedere una convalida più rigorosa).

Nella nostra applicazione, i biglietti sono stati convalidati utilizzando una firma, ma lo erano sincronizzato e impegnato nel database solo quando la rete era attiva. L'applicazione ha archiviato i ticket convalidati / di cui eseguire il commit in un bucket e ha cercato di eseguire il commit del maggior numero di ticket possibile una volta che la rete era disponibile. Evita anche di fare un INSERT per ticket.

All'estremità dell'ingresso all'evento, il wifi non arrivava affatto. Per risparmiare sui costi di avere un altro router per soli 10 metri in più di copertura, i dispositivi di scansione potrebbero comunicare tra loro e condividere le loro connessioni. Significa che se solo uno dei dispositivi avesse accesso al wifi, gli altri potrebbero teoricamente inviare i loro bucket ad esso, o al dispositivo più vicino nel raggio d'azione che lo inoltrerà.

La vita reale ci ha mostrato che la maggior parte di i dispositivi di scansione hanno perso la connessione almeno una volta in un minuto.

Quindi, in teoria, un ticket potrebbe essere scansionato da tutti i dispositivi non collegati che desidera, ma solo una volta per dispositivo. Questa è una condizione di competizione, ma molto più banale da sfruttare rispetto a quanto menzionato nelle altre risposte.


Una parola sul Come prevenirlo? :

Puoi prevenire la race condition, rimuovendo il parallelismo, che è ciò che fa un lock. Introdurrà latenze poiché stai sostanzialmente riducendo la capacità del tuo database di accettare scritture simultanee.

La domanda quindi diventa: ne vale la pena? Possiamo accettare un ritardo maggiore fino alla convalida per garantire la corretta autenticazione? Questo impedirà alle persone di trovare una porta aperta o saltare una recinzione?

+1 per l'esperienza di vita reale
+1 per l'idea del protocollo rete mesh / gossip se devi fare affidamento su una connessione di rete scadente
paolord
2019-07-31 08:06:54 UTC
view on stackexchange narkive permalink

Una buona soluzione potrebbe essere quella di utilizzare una coda di messaggi invece di inserire ritardi. Il ticket scansionato non viene elaborato immediatamente, attende che tutti i ticket inviati prima di essere elaborati. E il sistema non restituisce una risposta a meno che il ticket non abbia lasciato la coda e non sia stato elaborato correttamente. Un argomento a favore potrebbe essere lento perché tutti devono aspettare che gli altri finiscano. ma puoi logicamente frammentare gli ID dei biglietti, ad esempio: 2 code, 1 per i biglietti con numero dispari e 1 per quelli con numero pari. o semplicemente inserire i numeri di gruppo nell'ID stesso.

Per un evento con 1.000.000 di biglietti, anche un modesto PC desktop potrebbe eseguire un programma che costruisce un elenco di biglietti scansionati in memoria e controlla i duplicati, a una velocità di oltre un milione di biglietti al secondo.Anche un programma Javascript abbastanza semplicistico in un browser web può avvicinarsi a quella velocità (ho appena generato un milione di ticket casuali nel mio browser e ne ho contati 999.870 in meno di 1,2 secondi).Anche se ci fossero diecimila gate con persone che scansionano i biglietti, il ritardo nel caso peggiore sarebbe una frazione di secondo.
@supercat I cancelli devono comunicare tra loro.I biglietti di solito non sono riservati per un dato cancello.Il tuo semplicistico programma javascript consentirebbe di accettare diecimila copie dello stesso ticket.Come al solito, non è la CPU il fattore limitante: è l'IO.Attenzione, i sistemi di ticketing su cui ho lavorato dovevano essere tolleranti ai guasti di connessione, quindi dovevano sempre sbagliare dalla parte di "piuttosto lasciare entrare persone senza biglietti che tenere fuori persone senza biglietti".
@Luaan: No, sto dicendo che tutte le canalizzazioni di gate inviano richieste tramite un PC.Se un PC impiega meno di due microsecondi per elaborare ciascuna richiesta (un livello di prestazioni facilmente ottenibile in un semplice Javascript in un browser), o anche se impiega dieci volte quello, allora anche se le richieste sono state ricevute da tutti i 10.000 gate contemporaneamente, la manutenzionetutte queste richieste richiederebbero meno di un quarto di secondo.
cjs
2019-07-31 09:44:38 UTC
view on stackexchange narkive permalink

Questo è un tipico problema causato da una mancata corrispondenza tra il design del tuo sistema contabile (la "contabilità" qui sta monitorando l'uso di qualsiasi risorsa o oggetto, non solo denaro) e le informazioni del mondo reale che desideri monitorare.

Hai spiegato che nel mondo reale è chiaramente possibile che un codice venga scansionato due volte e desideri monitorare questa situazione, ma il tuo database memorizza solo "è stato scansionato". Questo non è corretto fino in fondo: SET is_scanned = TRUE WHERE ticket_id =? sarà effettivamente corretto (se nel mondo reale lo stato era "ticket_id? È stato scansionato" prima, e questo è ancora vero ora), ma in realtà non è quello che vuoi registrare.

Invece di mantenere uno stato, tieni un libro mastro: una tabella scan con intestazione ticket_id, scanned_timestamp , scanner_id , con la chiave che è tutti e tre questi campi.¹ Stai quindi registrando non solo che un biglietto è stato scansionato, ma ogni scansione del biglietto. Se non ci sono righe contenenti ticket_id =? , sai che il biglietto non è mai stato scansionato, altrimenti hai un elenco di tutte le volte che è stato scansionato (e dove).

Ora che il tuo database è in grado di modellare meglio le informazioni del mondo reale, puoi decidere cosa fare al riguardo. Il tuo particolare DBMS può offrire un modo per aggirare questa condizione di competizione ( INSERT ticket_id, scanner_id INTO scans WHERE ticket_id NOT IN scans o qualsiasi altra cosa²), nel qual caso eviti definitivamente due voci per lo stesso ticket_id. In alternativa, potresti semplicemente far entrare entrambe le persone e successivamente analizzare i tuoi record per capire il motivo per cui sembra che tu abbia due biglietti con lo stesso ticket_id.

Il modo in cui vuoi andare è in realtà una decisione aziendale, non tecnica. Ci sono molti motivi per cui potresti emettere in modo errato due biglietti validi con lo stesso ticket_id (ad esempio, un bug relativo ai tentativi di restituzione) o un biglietto deve essere utilizzato due volte dalla stessa persona (dimenticando di scansionare all'uscita). Se il numero di persone che tentano di imbrogliare il sistema è inferiore a un certo livello, potrebbe essere meglio per la tua azienda mangiare il costo di alcuni imbroglioni piuttosto che pagare il costo (in reputazione, azioni legali o altro) di negare un titolare di biglietto valido .

In breve: abbina il design del tuo database alla situazione reale del mondo, utilizzando libri contabili anziché stati in cui ciò si applica, e lascia che l'azienda prenda le decisioni aziendali su come affrontare queste situazioni del mondo reale.


¹Sto gesticolando qui su molti dettagli per mantenere la risposta breve ed esprimere solo l'idea generale.
² Presumo qui una strategia di coerenza appropriata che abbia lo stesso effetto come serializzare queste richieste INSERT in questa tabella.

L'OP non sta chiedendo di "tracciare" questo problema, ma di prevenirlo.Hai "agitato il braccio" sul meccanismo per prevenire la condizione di gara, che sarebbe la risposta.La tua nuova idea di tabella ha lo stesso problema delle condizioni di gara.
Questa strategia `INSERT` è anche soggetta a race condition, l'aggiunta di una chiave univoca su` scans (ticket_id) `dovrebbe essere sufficiente per risolverla, però.
@BenoitEsnard Devi _assolutamente no_ aggiungere una chiave univoca su `scans (ticket_id)`;questo è il punto centrale della mia risposta.E no, non ci sono condizioni di gara se usi il giusto modello di coerenza.Ho aggiornato la risposta per cercare di spiegare entrambi.
@schroeder Il punto è che la domanda è valida solo per determinate condizioni aziendali e il poster dovrebbe chiarire che ha preso in considerazione e preso esplicitamente quella decisione aziendale prima di affrontare questo problema, altrimenti non esiste.In genere la maggior parte delle persone che trovo ponendo questa domanda non lo ha fatto.
Sono d'accordo con altri commentatori sul fatto che questa strategia abbia ancora una condizione di gara;non esiste un "modello di coerenza corretto" discusso nella risposta che lo impedisca, a parte la singola frase che inizia "Il tuo particolare DBMS potrebbe ...".Se stai impedendo sia _e_ la registrazione, puoi utilizzare una delle altre risposte e creare un registro "tentativi di scansione" che può essere analizzato in seguito per vedere quante persone sono state allontanate.Quindi il dettaglio tecnico in questa risposta è un po 'una distrazione: in realtà è una _frame challenge_ della domanda, chiedendo se _any_ soluzione è effettivamente necessaria.
Džuris
2019-07-30 02:06:55 UTC
view on stackexchange narkive permalink

In Clojure hanno una funzionalità chiamata atomi per prendersi cura di questi problemi.

Entrano due thread. Entrambi leggono il valore is_scanned = false e procedono. Dopo aver elaborato il loro contenuto, restituiscono is_scanned = true per aggiornare atom. Tuttavia, un filo arriverà un po 'più velocemente.

Il thread che arriva un po 'più tardi avrà l'atomo cambiato, quindi quella funzione non sarà in grado di aggiornare l'atomo. Verrà invece detto di rieseguire se stesso con il valore ora aggiornato di is_scanned = true . E ora il ritorno sarà che l'altro biglietto non è più valido.

Ecco un video che spiega questa funzione di clojure.

Non sono davvero sicuro di come questo aiuti la persona che pone questa domanda in modo specifico, poiché hanno fortemente implicato che stanno usando un database relazionale e vogliono mettere lì la loro soluzione ... Né vedo come questo aiuti gli sviluppatori in generale a creare unsistema più sicuro, poiché le condizioni di gara non sono esclusive di Clojure (o di qualsiasi altra lingua) e Clojure non detiene il monopolio su come gestire le condizioni di gara.
Inoltre, anche se il tuo software è scritto in Clojure, cosa succede se hai così tante richieste da decidere di ridimensionare la tua applicazione orizzontalmente, eseguendo più nodi?Nessuna soluzione in memoria funzionerà;devi risolverlo nel database.


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 4.0 con cui è distribuito.
Loading...