Ho un progetto basato sul Web che consente agli utenti di lavorare sia online che offline e sto cercando un modo per generare ID univoci per i record sul lato client. Vorrei un approccio che funzioni mentre un utente è offline (cioè incapace di parlare con un server), è garantito per essere unico ed è sicuro. Per "sicuro", sono particolarmente preoccupato per i clienti che inviano ID duplicati (maliziosi o meno) e quindi causano il caos sull'integrità dei dati.
Ho cercato su Google, sperando che questo fosse già un problema risolto. Non ho trovato nulla di molto definitivo, soprattutto in termini di approcci in uso nei sistemi di produzione. Ho trovato alcuni esempi di sistemi in cui gli utenti accederanno solo ai dati che hanno creato (ad esempio un elenco Todo a cui si accede su più dispositivi, ma solo dall'utente che lo ha creato). Sfortunatamente, ho bisogno di qualcosa di un po 'più sofisticato. Qui ho trovato alcune idee davvero valide , in linea con il modo in cui pensavo che le cose potessero funzionare.
Di seguito è la mia soluzione proposta.
Alcuni requisiti
- Gli ID dovrebbero essere univoci a livello globale (o almeno univoci all'interno del sistema)
- Generato sul client (ovvero tramite javascript nel browser)
- Sicuro (come indicato sopra e altrimenti)
- I dati possono essere visualizzati / modificati da più utenti, inclusi gli utenti che non li hanno creati
- Non causa significativi problemi di prestazioni per i database back-end (come MongoDB o CouchDB)
La soluzione proposta
Quando gli utenti creano un account, viene loro assegnato un uuid che è stato generato dal server e noto per essere unico nel sistema. Questo ID NON deve essere uguale al token di autenticazione degli utenti. Chiamiamo questo id "token ID" dell'utente.
Quando un utente crea un nuovo record, genera un nuovo uuid in javascript (generato usando window.crypto quando disponibile. Vedi esempi qui ). Questo ID è concatenato con il "token ID" ricevuto dall'utente quando ha creato il proprio account. Questo nuovo ID composito (token ID lato server + uuid lato client) è ora l'identificatore univoco per il record. Quando l'utente è online e invia questo nuovo record al server back-end, il server dovrebbe:
- Identificalo come un'azione "inserisci" (ovvero non un aggiornamento o una cancellazione)
- Convalida entrambe le parti della chiave composita sono uuidi validi
- Convalida che la parte "token ID" fornita dell'ID composito sia corretta per l'utente corrente (ovvero corrisponde al token ID assegnato dal server all'utente quando ha creato il proprio account)
- Se tutto è copasetico, inserisci i dati nel db (facendo attenzione a fare un inserimento e non un "upsert" in modo che se l'id esiste già non aggiorni un record esistente per errore)
Query, aggiornamenti ed eliminazioni non richiederebbero alcuna logica speciale. Userebbero semplicemente l'id per il record allo stesso modo delle applicazioni tradizionali.
Quali sono i vantaggi di questo approccio?
Il codice client può creare nuovi dati offline e conoscere immediatamente l'id per quel record. Ho preso in considerazione approcci alternativi in cui un ID temporaneo sarebbe stato generato sul client che sarebbe stato successivamente sostituito con un ID "finale" quando il sistema era online. Tuttavia, questo sembrava molto fragile. Soprattutto quando inizi a pensare alla creazione di dati figlio con chiavi esterne che dovrebbero anche essere aggiornate. Per non parlare della gestione degli URL che cambieranno quando l'ID cambierà.
Rendendo gli ID un composto di un valore generato dal client E un valore generato dal server, ogni utente sta effettivamente creando ID in un sandbox. Questo ha lo scopo di limitare il danno che può essere fatto da un client malizioso / canaglia. Inoltre, eventuali collisioni ID sono per utente, non globali per l'intero sistema.
Poiché un token ID utente è associato al proprio account, gli ID possono essere generati nella sandbox di utenti solo da client autenticati (ovvero dove l'utente ha effettuato correttamente l'accesso). Questo ha lo scopo di impedire ai client malintenzionati di creare ID errati per un utente. Naturalmente, se un token di autenticazione dell'utente è stato rubato da un client dannoso, potrebbero fare cose cattive. Ma, una volta rubato un token di autenticazione, l'account viene comunque compromesso. Nel caso in cui ciò accadesse, il danno arrecato sarebbe limitato all'account compromesso (non all'intero sistema).
preoccupazioni
Ecco alcune delle mie preoccupazioni su questo approccio
Questo genererà ID sufficientemente unici per un'applicazione su larga scala? C'è qualche motivo per pensare che ciò comporterà collisioni id? Javascript può generare un uuid sufficientemente casuale perché funzioni? Sembra che window.crypto sia abbastanza ampiamente disponibile e questo progetto richiede già browser ragionevolmente moderni. ( questa domanda ora ha una sua domanda SO separata )
Ci sono delle lacune che mi mancano che potrebbero consentire a un utente malintenzionato di compromettere il sistema?
C'è motivo di preoccuparsi delle prestazioni del DB quando si esegue una query per una chiave composita composta da 2 uuidi. Come deve essere archiviato questo ID per le migliori prestazioni? Due campi separati o un singolo campo oggetto? Ci sarebbe un diverso approccio "migliore" per Mongo vs Couch? So che avere una chiave primaria non sequenziale può causare notevoli problemi di prestazioni durante gli inserimenti. Sarebbe più intelligente avere un valore generato automaticamente per la chiave primaria e memorizzare questo ID come campo separato? ( questa domanda ora ha una sua domanda SO separata )
Con questa strategia, sarebbe facile determinare che un determinato set di record è stato creato dallo stesso utente (poiché condividono tutti lo stesso token ID visibile pubblicamente). Anche se non vedo alcun problema immediato con questo, è sempre meglio non perdere più informazioni sui dettagli interni di quanto sia necessario. Un'altra possibilità sarebbe quella di eseguire l'hashing della chiave composita, ma sembra che potrebbe essere più un problema di quanto valga la pena.
Nel caso in cui vi sia una collisione id per un utente, non esiste un modo semplice per ripristinare. Suppongo che il client potrebbe generare un nuovo ID, ma questo sembra un sacco di lavoro per un caso limite che in realtà non dovrebbe mai accadere. Ho intenzione di lasciare questo senza indirizzo.
Solo gli utenti autenticati possono visualizzare e / o modificare i dati. Questa è una limitazione accettabile per il mio sistema.
Conclusione
È al di sopra di un piano ragionevole? Mi rendo conto che parte di questo si riduce a una sentenza basata su una comprensione più completa dell'applicazione in questione.