Solving the Double Submission Issue
Asked Answered
L

7

26

I would like to see how Web Developers avoid the double submission problem. So basically my understanding of the problem is as follows:

Double submission occurs when an impatient user submits a form multiple times, causing issues. This problem can be fixed by JavaScript (specifically jQuery scripts) that disable the submit button once the form has been submitted - a weakness of this is if clients have JavaScript disabled.

There is also server side methods of detection.

So my questions are:

How do people overcome double submission? What is a real life example of a problem caused by double submits? Do any Web Application Frameworks have double submission tools built in?

Lepton answered 15/1, 2011 at 11:36 Comment(2)
Real life example by paypal adding items to the shoppin cart multiple times,since user didn't get any response , while the first request is still under process.Blastocyst
See related question: #2831042Seidler
B
8

If you are working with java server side scripting and also using struts 2 then you refer this link which talks about on using token .

http://www.xinotes.org/notes/note/369/

A token should be generated and kept in session for the initial page render, when the request is submitted along with the token for the first time , in struts action run a thread with thread name as the token id and run the logic whatever the client has requested for , when client submit again the same request, check whether the thread is still running(thread.getcurrentthread().interrupted) if still running then send a client redirect 503.

Please look at the ExecuteAndWaitInterceptor of struts 2code, the logic of this combined with token will help out fast click

Blastocyst answered 15/1, 2011 at 12:40 Comment(12)
Tokens in sessions do not handle concurrency well. For example, multiple tabs in browsers.Cuevas
@Gustav: Can you explain, what u are talking abt multiple tabsBlastocyst
Oops, misread the answer. Thought you means a token in the universal session.Cuevas
@Gustav: session is not universal ,pertaining to a user logged in.Blastocyst
No, then I did understand it right. If one user opens multiple tabs in a browser, then the tokens can get mixed up because there is still only one user session.Cuevas
@Gustav: The token generated in the server side ,is also present in the cookie.Even if you open multiple tabs, the token is shared as the cookie is in the same domain path.Blastocyst
Yes, and that is a problem. I open page 1, looking to update my profile. Token 1 is generated. I open a new tab, looking to update my profile picture. Token 2 is generated and stored in the session. I submit tab 1, which sends token 1, which doesn't match token 2, so no change is committed. If I'm not lucky, profile page now generates token 3. I submit my photo, which sends token 2, which now doesn't match token 3.Cuevas
The question asked by user was for form submission : 1. u can have hidden fields inside html <form/> elements which prevents double submission 2. once user submits the form , i will redirect him to a page with the changes.Blastocyst
But tokens in session do not work always because they do not handle multiple windows.Cuevas
@Gustav:creating tab specific cookie. bytes.com/topic/javascript/answers/…Blastocyst
Your link shows Struts 1 code and you're talking about Struts 2. These 2 versions are incompatible.Carlie
The above link not working properly. I think they might have changed actual data from this website..Please update your answerNympholepsy
C
21

Real life situation: placing bets on a betting website. Users would double click and get two bets placed. Not good! Javascript checks were not sufficient to prevent this.

Solution:

  1. Create UUID/GUID hidden input in form using server-side scripting language that renders the form.

  2. On form submission immediately add this to a database table called UniqueSubmissions (for example). Then proceed with processing.

  3. Every subsequent request with the same UUID/GUID will be rejected if found in the UniqueSubmissions table.

This worked for us. Hope that helps answer your question!

Cataclysm answered 16/1, 2011 at 12:10 Comment(5)
Good! but whats UUID and GUID stand for?Lepton
Added links to UUID/GUID - most languages give you a way to programatically create one, for example I use ColdFusion and I can create one using createUUID().Cataclysm
Your idea is fine. But i think this architecture will face some limitations in Ajax response.How we can update UUID?GUID with out rendering page.Nympholepsy
this works fine for me: document.querySelector("#submitbutton").addEventListener("click",function(ev){ev.target.disabled=true;}); - assuming the page refreshes after clicking, as opposed to an XMLHttpRequest or simmilar responseHot
Why Javascript did not worked, what was the implementation ?Hyperbola
B
8

If you are working with java server side scripting and also using struts 2 then you refer this link which talks about on using token .

http://www.xinotes.org/notes/note/369/

A token should be generated and kept in session for the initial page render, when the request is submitted along with the token for the first time , in struts action run a thread with thread name as the token id and run the logic whatever the client has requested for , when client submit again the same request, check whether the thread is still running(thread.getcurrentthread().interrupted) if still running then send a client redirect 503.

Please look at the ExecuteAndWaitInterceptor of struts 2code, the logic of this combined with token will help out fast click

Blastocyst answered 15/1, 2011 at 12:40 Comment(12)
Tokens in sessions do not handle concurrency well. For example, multiple tabs in browsers.Cuevas
@Gustav: Can you explain, what u are talking abt multiple tabsBlastocyst
Oops, misread the answer. Thought you means a token in the universal session.Cuevas
@Gustav: session is not universal ,pertaining to a user logged in.Blastocyst
No, then I did understand it right. If one user opens multiple tabs in a browser, then the tokens can get mixed up because there is still only one user session.Cuevas
@Gustav: The token generated in the server side ,is also present in the cookie.Even if you open multiple tabs, the token is shared as the cookie is in the same domain path.Blastocyst
Yes, and that is a problem. I open page 1, looking to update my profile. Token 1 is generated. I open a new tab, looking to update my profile picture. Token 2 is generated and stored in the session. I submit tab 1, which sends token 1, which doesn't match token 2, so no change is committed. If I'm not lucky, profile page now generates token 3. I submit my photo, which sends token 2, which now doesn't match token 3.Cuevas
The question asked by user was for form submission : 1. u can have hidden fields inside html <form/> elements which prevents double submission 2. once user submits the form , i will redirect him to a page with the changes.Blastocyst
But tokens in session do not work always because they do not handle multiple windows.Cuevas
@Gustav:creating tab specific cookie. bytes.com/topic/javascript/answers/…Blastocyst
Your link shows Struts 1 code and you're talking about Struts 2. These 2 versions are incompatible.Carlie
The above link not working properly. I think they might have changed actual data from this website..Please update your answerNympholepsy
A
7

Use the redirect-after-post or sometimes called PRG (post/redirect/get)

In short, when the user posts the form, you perform a client side redirect (after consuming the post data) to the response (success) page.

Amphictyony answered 15/1, 2011 at 12:30 Comment(8)
Good Answer and certainly gives more depth than my "Server side methods". - Many Thanks.Lepton
this will not prevent fast double click issuesLemon
The redirect-after-post pattern is nice, but it doesn't solve this problem. The problem is either stupid users who double-click the submit button or impatient users who re-submit the form before the page has loaded. Both of these cases take place before the redirect-after-post pattern applies.Myocardiograph
You could just disable the submit button after the first click to handle fast double clicks.Amphictyony
@Joel: This is a solution, indeed but what happens if the user happens to have JavaScript disabled.Lepton
@JHarley - then I guess you'll need store some client side state as a hidden field on the form that you send back to the server to validate the post for uniqueness.Amphictyony
@JHarley the only ultimate solution is generating a random form uid/hash, put it as hidden, and mark it as treated once posted.Lemon
@JHarley one other solution is to always compare posted data with actual values in database and check wether or not you need to update values, but ths cannot always apply.Lemon
I
5

A real life example would be this answer posted twice ;-). If you don't want to rely on any aspect of the client side (javascript, or even cookies), you can calculate an MD5 hash of the data submitted, possibly by adding information such as source IP and the browser used, and reject posts that have the same hash.

Intemperance answered 15/1, 2011 at 13:3 Comment(0)
S
3

The web2py framework has built-in protection against double form submission. It stores a one-time token in the session as well as in a hidden field in the form, and they must match upon submission or the submission is rejected. This method also protects against CSRF (cross-site request forgery).

Scarletscarlett answered 6/2, 2011 at 2:47 Comment(4)
My problem with the one-time token in the session, is that if you have multiple pages open, the tokens can get mixed up. However, this is OK for platforms that can only see one screen at a time (like the old Kindle, or most WML based cellphone browsers.)Cuevas
If you'd like, you can give the form a unique name each time it is created, so opening the same form in a new window will not overwrite the key for the original form, allowing the form to be submitted from either window.Scarletscarlett
I'm not sure I understand what you are saying. Could you give me an example?Cuevas
import uuid form.process(formname=uuid.uuid4()). Now the form gets a unique name every time it is created, so if the same page is opened in multiple browser tabs/windows, the form has a different name on each page. The one-time token is associated with the form name, so opening a second page does not interfere with the token of the first page.Scarletscarlett
I
2

If the form has an intention of providing an interface for saving some data in server dbms, you may use special revision field that is mandatory for submitted data. A check whether or not the submitted revision matches that of the latest version of the data in the database (or is that of a new piece of data to be inserted), could provide you with good control of what to do if several submits are made in sequence.

Incontrollable answered 15/1, 2011 at 12:37 Comment(1)
PS this mechanism is mandatory in CouchDB and triggers on version conflictsIncontrollable
T
1

Using struts web-application framework we can handle this problem as follows:

Struts has 3 methods use for the token, saveToken(), isTokenValid() and resetToken().

saveToken() - generate the token key and save to request/session attribute.
isTokenValid() - validate submitted token key against the 1 store in request/session.
resetToken() - reset the token key.

How it works:
1) Upon loading the form, invokes saveToken() on the action class to create and store the token key. Struts will store the generated key in request/session. If the token successfully created, when view source on the browser you will see something similar to the following, the token key is stored as a hidden field:

<form action="myaction.do" method="post"> 
 <input type="hidden" 
 name="<%= Constants.TOKEN_KEY %>" 
 value="<%= session.getAttribute(Action.TRANSACTION_TOKEN_KEY) %>" > 

2) Once the form submitted, invokes isTokenValid() on the action class, it will validate the submitted token key(hidden field) with the token key stored previously on request/session. If match, it will return true.

public final ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
    saveToken(request);
    if (!tokenIsValid(request)) {
        //forward to error page saying "your transaction is already being processed" 
    } else {
        //process action 
        //forward to jsp 
    }
    // Reset token after transaction success. 
    resetToken(request);
}

reference

Taritariff answered 13/8, 2015 at 11:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.