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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
JHarley1
  • 2,026
  • 6
  • 24
  • 34
  • 1
    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. – Dead Programmer Jan 15 '11 at 13:10
  • See related question: http://stackoverflow.com/questions/2830542/prevent-double-submission-of-forms-in-jquery – Nathan Long Jan 18 '12 at 14:23

7 Answers7

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!

Ciaran Archer
  • 12,316
  • 9
  • 38
  • 55
  • Good! but whats UUID and GUID stand for? – JHarley1 Jan 16 '11 at 13:40
  • 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()`. – Ciaran Archer Jan 16 '11 at 17:38
  • 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. – Sobin Sunny Dec 03 '15 at 09:54
  • 1
    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 response – hanshenrik Aug 27 '17 at 11:15
  • Why Javascript did not worked, what was the implementation ? – Muflix Jul 24 '18 at 12:17
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

Dead Programmer
  • 12,427
  • 23
  • 80
  • 112
  • 1
    Tokens in sessions do not handle concurrency well. For example, multiple tabs in browsers. – Gustav Bertram Nov 17 '11 at 11:35
  • @Gustav: Can you explain, what u are talking abt multiple tabs – Dead Programmer Nov 17 '11 at 14:43
  • Oops, misread the answer. Thought you means a token in the universal session. – Gustav Bertram Nov 17 '11 at 14:50
  • @Gustav: session is not universal ,pertaining to a user logged in. – Dead Programmer Nov 17 '11 at 15:33
  • 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. – Gustav Bertram Nov 17 '11 at 21:44
  • @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. – Dead Programmer Nov 18 '11 at 05:27
  • 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. – Gustav Bertram Nov 18 '11 at 08:06
  • The question asked by user was for form submission : 1. u can have hidden fields inside html
    elements which prevents double submission 2. once user submits the form , i will redirect him to a page with the changes.
    – Dead Programmer Nov 18 '11 at 08:18
  • But tokens in session do not work always *because* they do not handle multiple windows. – Gustav Bertram Nov 18 '11 at 08:30
  • @Gustav:creating tab specific cookie. http://bytes.com/topic/javascript/answers/878108-creating-browser-tab-specific-cookie – Dead Programmer Nov 18 '11 at 08:43
  • Your link shows Struts 1 code and you're talking about Struts 2. These 2 versions are incompatible. – Buhake Sindi Jan 04 '12 at 13:53
  • 1
    The above link not working properly. I think they might have changed actual data from this website..Please update your answer – Sobin Sunny Dec 03 '15 at 09:45
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.

Joel
  • 29,538
  • 35
  • 110
  • 138
  • Good Answer and certainly gives more depth than my "Server side methods". - Many Thanks. – JHarley1 Jan 15 '11 at 12:33
  • 12
    this will not prevent fast double click issues – regilero Jan 15 '11 at 12:33
  • 3
    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. – Sean Patrick Floyd Jan 15 '11 at 12:37
  • 1
    You could just disable the submit button after the first click to handle fast double clicks. – Joel Jan 15 '11 at 12:37
  • @Joel: This is a solution, indeed but what happens if the user happens to have JavaScript disabled. – JHarley1 Jan 15 '11 at 12:38
  • 1
    @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. – Joel Jan 15 '11 at 12:40
  • @JHarley the only ultimate solution is generating a random form uid/hash, put it as hidden, and mark it as treated once posted. – regilero Jan 15 '11 at 12:47
  • @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. – regilero Jan 15 '11 at 12:49
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.

Yack
  • 109
  • 3
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).

Anthony
  • 25,466
  • 3
  • 28
  • 57
  • 2
    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.) – Gustav Bertram Nov 17 '11 at 11:12
  • 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. – Anthony Nov 17 '11 at 16:07
  • I'm not sure I understand what you are saying. Could you give me an example? – Gustav Bertram Nov 18 '11 at 08:10
  • 1
    `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. – Anthony Nov 18 '11 at 14:02
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.

Dennis Kreminsky
  • 2,117
  • 15
  • 23
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

Premraj
  • 72,055
  • 26
  • 237
  • 180