3

Yes others have asked similar questions, however, the end solution was using JavaScript. I have that working, my question becomes what happens when the user has JavaScript turned off? I would hope only advanced users would be turning off JavaScript and thus know to know click once on a button and can tell that the server is working. On the off chance they don't, how do I make sure that button is only clicked once?

I should note that this is on a payment form.

Community
  • 1
  • 1
Mike Wills
  • 20,959
  • 28
  • 93
  • 149

4 Answers4

4

I'm afraid without JavaScript there is no way to prevent this. If the click results in a POST request, then you can try to make it idempotent on the server side.

Dennis Traub
  • 50,557
  • 7
  • 93
  • 108
2

You cannot make sure the button is only clicked once, as you have no control over user's browser. What you can do, though, is to add a hidden field, a token to your forms so that if you see a token you've already seen, you'll be able to return an already-calculated answer.

Update: In case of payment processing, it's not even a technique for preventing double submission—it's a technique protecting your clients from fraud. Check out OWASP's A5: Cross-Site Request Forgery (CSRF):

Preventing CSRF requires the inclusion of a unpredictable token in the body or URL of each HTTP request. Such tokens should at a minimum be unique per user session, but can also be unique per request.

  1. The preferred option is to include the unique token in a hidden field. This causes the value to be sent in the body of the HTTP request, avoiding its inclusion in the URL, which is subject to exposure.

  2. The unique token can also be included in the URL itself, or a URL parameter. However, such placement runs the risk that the URL will be exposed to an attacker, thus compromising the secret token.

Basically, each time you receive a payment form, you want to make sure it's a legitimate response to the form you've shown. Handling double submission comes free with security—a rare case indeed! ;)

alf
  • 8,377
  • 24
  • 45
  • Hmm... that sounds really complicated for the select few that disable javascript. Perhaps with the preauth/postauth method of processing credit cards, we will be safer from double charges. – Mike Wills Oct 18 '11 at 20:09
  • It is indeed. In most cases, you just don't care. When you do, though, you're usually trying to create a layer which would inject token in your forms—and intercept all the requests checking for the tokens, so that the rest of the code would not need to care. – alf Oct 18 '11 at 20:17
0

For a simple and small ASP.NET MVC app, I find using the HttpRuntime.Cache is enough:

Function SomeAction() As ActionResult
    Dim cachekey = "uniquecode"
    If HttpRuntime.Cache(cachekey) IsNot Nothing Then
        ' wait until the other request is finished
        Do
            Threading.Thread.Sleep(1000)
        Loop Until HttpRuntime.Cache(cachekey) Is Nothing
    End If

    HttpRuntime.Cache.Add(
        cachekey, "", Nothing,
        DateTime.Now.AddMinutes(5),
        Cache.NoSlidingExpiration, CacheItemPriority.Low, Nothing)

    Try

        ' do stuff

    Finally
        HttpRuntime.Cache.Remove(cachekey)
    End Try
End Function

The cachekey must be the same for requests that you consider double, and must be different from all the other requests. If you check that the cachekey is already in the cache, tell the thread that is processing the request to wait (or throw error).

Endy Tjahjono
  • 24,120
  • 23
  • 83
  • 123
0

what happens when the user has JavaScript turned off?

The server is hit twice and there is not much you could do about it.

Now depending on what you are doing on the server there are different ways to react. For example in a RESTful application if you are using a POST verb which modifies some state on the server and is neither safe nor idempotent it is eventually the underlying data source that will detect the anomaly and simply throw an exception which will be gracefully reported to the user telling him that his request was already submitted.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928