Dubbele entry in database voorkomen

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Torrentus
  • Registratie: April 2009
  • Laatst online: 13:06
Beste Tweakers,

Ik hoop in het goede forum te zitten, ben een beginner in de database/mySQL wereld, maar het loopt vrij lekker tot noch toe. Loop nu echter wel tegen een probleem op:

Ik heb een formulier gemaakt, als stap 1 van een bestelproces. Zodra het formulier correct ingevuld is, gevalideerd is en in de database gestopt is, gaat de gebruiker naar stap 2. Als de gebruiker zich daar bedenkt, gebruikt hij ofwel de back-button in zijn browser, of een soortgelijke knop op de website om terug te gaan naar de vorige pagina. Als hij daar dan een fout corrigeert (bijvoorbeeld een typfout in zijn postcode, of in zijn naam), en weer op volgende drukt, heb ik twee entry's in mijn database staan, met typfout en zonder.

Op een of andere manier moet de eerste entry dus verwijdert worden op het moment dat de gebruiker opnieuw submit met (iets) andere gegevens.

Ik heb natuurlijk gezocht hierover, maar vind vooral oplossingen voor gebruikers die duplicate entries veroorzaken; exact dezelfde gegevens nogmaals. Dat is in mijn geval echter niet het probleem, dat is omzeilt met een goed ingestelde primary.

Ik hoop dat jullie mij op weg kunnen helpen.. :)

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Verwijderd

Zet een verborgen veld in het formulier. Dat geef je een random waarde. Als het formulier wordt ingevuld, maak je de entry aan en voeg je de random waarde toe aan die entry. Zo weet je welk formulier bij welke entry hoorde, zelfs voordat de insert ID / autoincrement waarde van de entry bekend was.

Als iemand teruggaat, iets aanpast en het formulier opnieuw submit, dan weet je precies welke entry moet worden aangepast. Kortom, bij het verwerken van het formulier moet je checken of de entry al bestaat (aan de hand van de verborgen waarde). Zo ja, update entry, zo nee, insert entry.

Acties:
  • 0 Henk 'm!

  • CodeCaster
  • Registratie: Juni 2003
  • Niet online

CodeCaster

Can I get uhm...

Een optie is om deze headers op je invulpagina's toe te passen, zodat bij een Back-klik de pagina altijd opnieuw wordt opgevraagd:
PHP:
1
2
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");


Als je dat combineert met het opslaan van de $_POST-waarden in een sessie, hoef je pas bij de laatste stap de data in de database op te slaan. Als ze voor het voltooien op Back klikken, vul je de waarden van de velden die je in je sessie hebt staan.

Je houdt dan ook in de sessie bij dat de bestelling is voltooid, en als ze dan nog via Back op een van de bestelpagina's komen, leeg je hun winkelmandje (en de sessie) en krijgen ze weer een leeg besteformulier te zien.

Wokkels. :w

https://oneerlijkewoz.nl
Op papier is hij aan het tekenen, maar in de praktijk...


Acties:
  • 0 Henk 'm!

  • Wokkels
  • Registratie: Juli 2000
  • Laatst online: 29-10-2024

Wokkels

Het lekkerste zoutje

Ik zou de informatie pas in de database stoppen als het bestelproces voltooid is. Dat betekent dat je de data tijdelijk in een sessie oid moet opslaan tot de klant daadwerkelijk helemaal klaar is met de bestelling. Lijkt me sowieso beter dan een hidden random veld. Buiten dat voorkomt het ook flinke vervuiling van je database als iemand het bestellen halverwege afbreekt door bijvoorbeeld de browser te sluiten.

CodeCaster :w

[ Voor 6% gewijzigd door Wokkels op 14-07-2012 18:38 ]

Permanent wintericon!


Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Verwijderd schreef op zaterdag 14 juli 2012 @ 18:25:
Zet een verborgen veld in het formulier. Dat geef je een random waarde. Als het formulier wordt ingevuld, maak je de entry aan en voeg je de random waarde toe aan die entry. Zo weet je welk formulier bij welke entry hoorde, zelfs voordat de insert ID / autoincrement waarde van de entry bekend was.

Als iemand teruggaat, iets aanpast en het formulier opnieuw submit, dan weet je precies welke entry moet worden aangepast. Kortom, bij het verwerken van het formulier moet je checken of de entry al bestaat (aan de hand van de verborgen waarde). Zo ja, update entry, zo nee, insert entry.
Twee zaken waar dit advies niet ver genoeg op in gaat:
Ten eerste is het een potentieel veiligheidslek; iedereen kan met de hand een POST request fabriceren met een andere identifier en zo wellicht bijvoorbeeld een bezorgadres van een bestaande bestelling wijzigen. Je zult extra stappen moeten maken om dit scenario te voorkomen. Identifiers koppelen aan een gebruikerssessie, misschien?

Ten tweede heb je een heel goede random number generator nodig om collisies in je identifiers te vermijden. Misschien een GUID, maar die is wellicht niet betrouwbaar genoeg onder het platform waar je mee werkt. Je zou alvast een row in je database table kunnen reserveren en het auto-incremented ID kunnen gebruiken, maar dat maakt het eerste veiligheidsprobleem alleen maar groter vanwege de voorspelbaarheid van reeds in gebruik zijnde identifiers.


Het beste is imho nog steeds gewoon e.e.a. in de session (server-side, of via een cookie/sessionstorage client-side) bij te houden en pas in de laatste stap te verwerken.

[ Voor 5% gewijzigd door R4gnax op 15-07-2012 00:39 ]


Acties:
  • 0 Henk 'm!

Verwijderd

CodeCaster schreef op zaterdag 14 juli 2012 @ 18:36:
Een optie is om deze headers op je invulpagina's toe te passen, zodat bij een Back-klik de pagina altijd opnieuw wordt opgevraagd:
PHP:
1
2
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
Misschien zijn anno 2012 deze headers wel betrouwbaar. Het is een goede suggestie ze te gebruiken, eigenlijk sowieso bij "volatile" content, maar zeker bij een webshop. Ik kan me in het verleden echter situaties herinneren waarin browsers erg eigenwijs waren (lees: IE) en zich niet altijd aan deze vriendelijke verzoeken om niet te cachen hielden. Hopelijk is dat tegenwoordig wel zo.
Als je dat combineert met het opslaan van de $_POST-waarden in een sessie, hoef je pas bij de laatste stap de data in de database op te slaan. Als ze voor het voltooien op Back klikken, vul je de waarden van de velden die je in je sessie hebt staan.

Je houdt dan ook in de sessie bij dat de bestelling is voltooid, en als ze dan nog via Back op een van de bestelpagina's komen, leeg je hun winkelmandje (en de sessie) en krijgen ze weer een leeg besteformulier te zien.
Alleen heeft niet elke webshop een winkelmandje. Een select aantal webshops heeft die ook helemaal niet nodig, en hetzelfde geldt voor sessies :)
R4gnax schreef op zondag 15 juli 2012 @ 00:38:
Twee zaken waar dit advies niet ver genoeg op in gaat:
Ten eerste is het een potentieel veiligheidslek; iedereen kan met de hand een POST request fabriceren met een andere identifier en zo wellicht bijvoorbeeld een bezorgadres van een bestaande bestelling wijzigen.
Dit moet je altijd al doen. Vind je het heel erg dat ik ook niets heb gezegd over het controleren van user input, het voorkomen van SQL injectie, de rechten op de bestanden en alle andere 600 potentiële veiligheidsrisico's van elke webapplicatie?
Je zult extra stappen moeten maken om dit scenario te voorkomen. Identifiers koppelen aan een gebruikerssessie, misschien?
En dan heb je ineens een sessie en cookie policy nodig. Iets dat tot nu toe misschien niet eens hoefde.
Ten tweede heb je een heel goede random number generator nodig om collisies in je identifiers te vermijden. Misschien een GUID, maar die is wellicht niet betrouwbaar genoeg onder het platform waar je mee werkt.
De betrouwbaarheid van je RNG is altijd een issue en die geldt net zo hard voor de session ID die eveneens gewoon bestaat uit een random zooitje bits.
Je zou alvast een row in je database table kunnen reserveren en het auto-incremented ID kunnen gebruiken, maar dat maakt het eerste veiligheidsprobleem alleen maar groter vanwege de voorspelbaarheid van reeds in gebruik zijnde identifiers.
Hier ga ik verder maar niet op in. Hoewel veiligheid altijd een issue is, is dat hier niet relevant. Het gaat er niet om dat iemand niet een bestelling van een ander iemand mag kunnen wijzigen. Dat moet je sowieso te allen tijde voorkomen. Het gaat erom dat een gebruiker misschien niet twee keer een bestelling wilde doen maar alleen een wijziging wilde doorvoeren.

Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Verwijderd schreef op zondag 15 juli 2012 @ 07:52:
[...]

Misschien zijn anno 2012 deze headers wel betrouwbaar. Het is een goede suggestie ze te gebruiken, eigenlijk sowieso bij "volatile" content, maar zeker bij een webshop. Ik kan me in het verleden echter situaties herinneren waarin browsers erg eigenwijs waren (lees: IE) en zich niet altijd aan deze vriendelijke verzoeken om niet te cachen hielden. Hopelijk is dat tegenwoordig wel zo.
IE 5.5 en ouder hebben inderdaad in verschillende mate problemen met de cache control header. Vanaf IE6 werkt het gewoon correct.
Verwijderd schreef op zondag 15 juli 2012 @ 07:52:
Dit moet je altijd al doen. Vind je het heel erg dat ik ook niets heb gezegd over het controleren van user input, het voorkomen van SQL injectie, de rechten op de bestanden en alle andere 600 potentiële veiligheidsrisico's van elke webapplicatie?
Nee. Ik vind het alleen erg dat je een advies geeft wat wanneer het verbatim gevolgd wordt, direct aanstuurt op het introduceren van een veiligheidslek. Zet er dan gewoon nog bij dat er wel nog wat security issues bij komen kijken die verder uitgewerkt moeten worden. Je kunt het bij een vage beschrijving laten, maar dan is in elk geval duidelijk dat je suggestie niet verbatim geimplementeerd dient te worden.
Verwijderd schreef op zondag 15 juli 2012 @ 07:52:
[...]

En dan heb je ineens een sessie en cookie policy nodig. Iets dat tot nu toe misschien niet eens hoefde.

[...]

De betrouwbaarheid van je RNG is altijd een issue en die geldt net zo hard voor de session ID die eveneens gewoon bestaat uit een random zooitje bits.
Ik neem aan dat je als beetje webshop toch gebruikersaccounts zult hebben. Dan heb je dus naar alle waarschijnlijkheid al een sessie & cookie policy. Dus... waarom genereren we zelf ook al weer een extra random getal met mogelijkheid tot collisions en wat eenvoudig gespoofed kan worden?
Verwijderd schreef op zondag 15 juli 2012 @ 07:52:
Hier ga ik verder maar niet op in. Hoewel veiligheid altijd een issue is, is dat hier niet relevant. Het gaat er niet om dat iemand niet een bestelling van een ander iemand mag kunnen wijzigen. Dat moet je sowieso te allen tijde voorkomen. Het gaat erom dat een gebruiker misschien niet twee keer een bestelling wilde doen maar alleen een wijziging wilde doorvoeren.
Het illustreert anders keurig hoe de keuze voor de wijze van tijdelijke opslag van gegevens voor een bestelling in wording ook zijn weerslag heeft op security van je webshop. Het is een goed argument om niet je auto-incremented ID als zodanig aan clients bloot te stellen. Hoezo is dat niet relevant?

offtopic:
Oh en btw; security is altijd relevant. De uitspraak "secrity is hier niet relevant" ligt ten grondslag aan het feit dat we anno 2012 bijvoorbeeld nog steeds met SQL injectie te maken hebben. Of niet, Yahoo?

Acties:
  • 0 Henk 'm!

Verwijderd

R4gnax schreef op zondag 15 juli 2012 @ 14:02:

Nee. Ik vind het alleen erg dat je een advies geeft wat wanneer het verbatim gevolgd wordt, direct aanstuurt op het introduceren van een veiligheidslek.
Ik vind dat dat wel meevalt. Misschien had ik erbij moeten vermelden dat de random waarde niet door derden geraden moet kunnen worden, veel meer dan dat ook niet.
Zet er dan gewoon nog bij dat er wel nog wat security issues bij komen kijken die verder uitgewerkt moeten worden. Je kunt het bij een vage beschrijving laten, maar dan is in elk geval duidelijk dat je suggestie niet verbatim geimplementeerd dient te worden.
Dat is altijd zo. Maar ik heb niet altijd zin om alle kennis die ik heb in één post te gaan proppen.
Ik neem aan dat je als beetje webshop toch gebruikersaccounts zult hebben.
Ik snap niet waarom je die aanname doet. Er zijn zat voorbeelden te bedenken waarin iemand geen gebruikersaccount hoeft te hebben om een bestelling te kunnen plaatsen. Bijvoorbeeld als de klant wil dat het zo eenvoudig mogelijk moet zijn om te bestellen, en er dus niet eerst geregistreerd hoeft te worden.
Er zijn genoeg mensen die niet eerst een hele procedure door willen om bijvoorbeeld een pizza te kunnen bestellen.
Dan heb je dus naar alle waarschijnlijkheid al een sessie & cookie policy. Dus... waarom genereren we zelf ook al weer een extra random getal met mogelijkheid tot collisions en wat eenvoudig gespoofed kan worden?
Eenvoudig? Dat ligt maar net aan het aantal random bitjes. Volgens mij zei ik daar helemaal niets over.
Het illustreert anders keurig hoe de keuze voor de wijze van tijdelijke opslag van gegevens voor een bestelling in wording ook zijn weerslag heeft op security van je webshop. Het is een goed argument om niet je auto-incremented ID als zodanig aan clients bloot te stellen. Hoezo is dat niet relevant?
Ik ben niet de idioot die met het voorstel kwam om vooraf een record aan te maken om maar een autoincrement ID te verkrijgen. Vind je het heel erg dat ik daar niet op doorga omdat het een belachelijk slecht idee is?
Oh en btw; security is altijd relevant.
Dat zei ik al ja. Misschien moet je dat nog maar eens lezen, want dat zei ik al ja. Twee keer zelfs.
De uitspraak "security is hier niet relevant" ligt ten grondslag aan het feit dat we anno 2012 bijvoorbeeld nog steeds met SQL injectie te maken hebben.
Wil je alsjeblieft voortaan niet de hele alinea samenvatten door de context weg te laten, aangezien de rest van de alinea nogal belangrijk was en extra opheldering gaf? Dat stond er niet voor niets.

Ik heb voor honderden websites en applicaties mailtjes gestuurd naar de makers danwel de beheerders. Enkelen daarvan gingen over het vooraf aanmaken van records waardoor het vrij eenvoudig was een harddisk vol te pompen met onzinnige records via cross-site request forgery.

En aangezien je daar op een bestelpagina (waar je niet altijd met sessies te maken hebt, weet ik uit ervaring) toch al rekening mee moet houden, voegt het voorstel dat ik deed nul komma nul extra risico toe. Sterker nog, het is prima mogelijk dat juist te gebruiken tegen xsrf. Maar nogmaals, ik ga niet al mijn kennis samenvatten in één post. Het ging om een simpele techniek, niet over security.

Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Verwijderd schreef op zondag 15 juli 2012 @ 15:07:
Ik ben niet de idioot die met het voorstel kwam om vooraf een record aan te maken om maar een autoincrement ID te verkrijgen. Vind je het heel erg dat ik daar niet op doorga omdat het een belachelijk slecht idee is?
Ik haalde dat idee inderdaad zelf aan, en schoot het daarna direct af. Waarom dan deze moeite doen? Zodat de TS, die zelf aangeeft een beginner te zijn, deze fout zeker niet zou gaan maken. Ondanks dat het inderdaad een idioot voorstel is, wordt deze fout namelijk nog al eens begaan. (Met name omdat er wanneer dit soort beslissingen ad hoc tijdens het bouwen genomen worden, vaak even niet op het totaalbeeld gelet wordt.)
Verwijderd schreef op zondag 15 juli 2012 @ 15:07:
Dat zei ik al ja. Misschien moet je dat nog maar eens lezen, want dat zei ik al ja. Twee keer zelfs.
Heb het nogmaals gelezen en dat zei je inderdaad. Excuses; daar was ik iets te snel tot conclusies gesprongen.
Verwijderd schreef op zondag 15 juli 2012 @ 15:07:
Maar nogmaals, ik ga niet al mijn kennis samenvatten in één post. Het ging om een simpele techniek, niet over security.
Hier geef je alleen tussen de regels door te kennen dat de techniek toch niet zo simpel is. ;) Het basis principe is misschien simpel, maar het haakt gelijk in op veel andere zaken (waaronder het security vlak) die moeilijk samen te vatten zijn en waarvoor je meer kennis van zaken moet hebben.

Je hebt overigens gelijk; als je het goed aanpakt, dan kun je dit inderdaad netjes sessieloos opzetten zonder het risico te lopen je database vol te laten lopen met onzinnige records. Dat ben ik 100% met je eens. Het is alleen niet de makkelijkste methode en waarschijnlijk ook niet het eerste waar iemand aan zou denken.

[ Voor 9% gewijzigd door R4gnax op 15-07-2012 17:16 ]

Pagina: 1