2008-11-20 17 views
5

Sto assemblando una piccola app Web che scrive su un database (Perl CGI & MySQL). Lo script CGI prende alcune informazioni da un modulo e lo scrive in un database. Tuttavia, noto che se faccio clic su "Ricarica" ​​o "Indietro" sul browser Web, scriverà nuovamente i dati nel database. Non voglio questo.Come posso evitare che il database venga nuovamente scritto quando il browser esegue una ricarica/ritorno?

Qual è il modo migliore per proteggere i dati da riscrivere in questo caso?

risposta

17

Non utilizzare le richieste GET per apportare modifiche! Be RESTful; usa POST (o PUT) invece il browser dovrebbe avvisare l'utente di non ricaricare la richiesta. Il reindirizzamento (using HTTP redirection) a una pagina di ricevuta utilizzando una normale richiesta GET dopo una richiesta POST/PUT renderà possibile aggiornare la pagina senza essere avvisati sulla reinvio.

EDIT:

Presumo l'utente è connesso in qualche modo, e quindi si ha giá qualche modo di tracciare l'utente, ad esempio, sessione o simili.

Si potrebbe fare un timestamp (o un hash casuale, ecc ..), quando si visualizza il modulo di memorizzazione sia come un campo nascosto (appena oltre l'anti cross-site request gettone Sono sicuro che giá ci sono) e in una variabile di sessione (che è memorizzata in modo sicuro sul tuo server), quando ricevi una richiesta POST/PUT per questo modulo, controlli che il timestamp sia lo stesso di quello in sessione. Se lo è, si imposta il timestamp nella sessione su qualcosa di variabile e difficile da indovinare (timestamp concatenato con una stringa segreta per esempio), quindi è possibile salvare i dati del modulo. Se qualcuno ripete la richiesta ora non troverà lo stesso valore nella variabile di sessione e negherà la richiesta.

Il problema con questo è che il modulo non è valido se l'utente fa clic indietro per modificare qualcosa, e potrebbe essere un po 'troppo duro, a meno che non si stia aggiornando. Quindi, se hai problemi con gli utenti "stupidi" che aggiornano e fanno clic sul pulsante indietro, quindi ripubblicando accidentalmente qualcosa, basta usare il POST per ricordare loro di non farlo, e il reindirizzamento lo renderà meno probabile. Se hai un problema con utenti malintenzionati, dovresti usare anche un timestampt, anche se a volte confonderà gli utenti, anche se gli utenti pubblicano deliberatamente lo stesso messaggio più volte e probabilmente devi trovare un modo per vietarli. Usare il POST, avere un timestam e persino fare un confronto completo dell'intero database per verificare la presenza di post duplicati, non aiuta affatto se gli utenti malintenzionati scrivono uno script per caricare il modulo e inviare automaticamente i rifiuti casuali, automaticamente. (Ma la protezione delle richieste incrociate rende molto più difficile)

+0

L'OP si lamenta dei suoi utenti che incasinano il sistema. Dire "fai in modo che il browser provi e fermali" non è certo una soluzione in grado di risolvere il problema. –

+2

No, ma è meno probabile che gli utenti benevoli lo facciano, ho aggiunto alcune idee per ridurre ulteriormente il problema. –

4

Trovo utile tenere traccia del numero di invii di moduli che l'utente ha eseguito nella propria sessione. Quindi durante il rendering del modulo creo un campo nascosto che contiene quel numero. Se l'utente quindi invia nuovamente il modulo premendo il pulsante Indietro, invierà il vecchio # e il server può dire che l'utente ha già inviato il modulo esaminando ciò che è nella sessione a ciò che il modulo sta dicendo.

Solo i miei 2 centesimi.

6

L'utilizzo di una richiesta POST farà sì che il browser provi a impedire all'utente di inviare nuovamente la stessa richiesta, ma mi consiglia di utilizzare il monitoraggio delle transazioni basato sulla sessione di qualche tipo in modo che se l'utente ignora gli avvisi dal browser e invia nuovamente la sua domanda la tua applicazione impedirà la duplicazione delle modifiche al database. È possibile includere un input nascosto nel modulo di invio con il valore impostato su un hash crittografico e registrare tale hash se la richiesta viene inoltrata ed elaborata senza errori.

1

Se non si utilizza già una sorta di gestione delle sessioni (che consente di prendere nota e tenere traccia degli invii di moduli), una soluzione semplice sarebbe quella di includere una sorta di identificativo univoco nel modulo (come elemento nascosto) questo è parte della transazione principale del DB stesso o tracciata in una tabella DB separata. Quindi, quando ti viene inviato un modulo, controlli l'ID univoco per vedere se è già stato elaborato. E ogni volta che il modulo stesso viene reso, devi solo assicurarti di avere un ID univoco.

+0

Si emette un numero di biglietto, quindi si punch il ticket una sola volta. – dkretz

1

Prima di tutto, non ci si può fidare del browser, quindi qualsiasi discorso sull'uso del POST piuttosto che GET è per lo più nerd flim-flam. Sì, il cliente potrebbe ricevere un avvertimento sulla falsariga di "intendevi reinviare nuovamente questi dati?", Ma probabilmente diranno "Sì, ora lasciami in pace, stupido computer".

E giustamente: se non si desidera inviare invii duplicati, è il problema da risolvere, non quello dell'utente.

Probabilmente hai qualche idea su cosa significhi essere una presentazione duplicata. Forse è lo stesso IP in pochi secondi, forse è lo stesso titolo di un post di un blog o di un URL che è stato inviato di recente. Forse è una combinazione di valori, ad es. Indirizzo IP, indirizzo email e titolo soggetto di un modulo di contatto inviato. In entrambi i casi, se hai individuato manualmente alcuni duplicati nei tuoi dati, dovresti essere in grado di trovare un modo per identificare a livello di codice un duplicato al momento dell'invio e contrassegnarlo per l'approvazione manuale (se non sei sicuro), o semplicemente dicendo al mittente "Hai fatto doppio clic?" (Se le informazioni non sono sorprendentemente riservate, potresti presentare il record esistente che hai per loro e dire "È questo che intendevi inviarci? Se è così, l'hai già fatto - evviva")

1

I 'non fare affidamento sugli avvisi POST dal browser. Gli utenti devono semplicemente fare clic su OK per far scomparire i messaggi.

Ogni volta che avrai una richiesta che deve essere una sola volta, ad esempio, "effettua un pagamento", invia un token univoco, che viene inviato di nuovo con la richiesta. Lancia il token dopo che è tornato, e così ora puoi dire quando qualcosa è una sottomissione valida (qualsiasi cosa con un token che non sia 'attivo'). Scadenza dei token attivi dopo X quantità di tempo, ad es. quando termina una sessione utente.

(alternativamente monitorare i gettoni che sono venuti di nuovo, e se avete ricevuto prima di allora non è valido.)

+0

C'è ancora un problema con le condizioni di gara, però. Non appena si riceve il token, è necessario verificare che non sia stato usato in precedenza, ad es. emettere un comando di aggiornamento SQL e verificare se il valore restituito è 1 (hai cambiato qualcosa) o 0E0 (hai provato a cambiarlo a ciò che era già). –

0

fare un post ogni volta che si modifica dei dati, ma mai restituire una risposta HTML da un post. .. restituisce invece un reindirizzamento a un GET che recupera i dati aggiornati come pagina di conferma. In questo modo, non c'è da preoccuparsi per loro di aggiornare la pagina. Se si aggiornano, tutto ciò che accadrà è un altro recupero, mai un'azione che modifica i dati.

Problemi correlati