6 ms·
HTTP already handles this just fine if you have sensitive forms: your form can include a one-time token which your server validates. If the token has already be
by jxf 12y ago
HTTP already handles this just fine if you have sensitive forms: your form can include a one-time token which your server validates. If the token has already been used, you don't process the second request.
What we definitely shouldn't do (as the author suggests) is disable form submissions on subsequent clicks. What if the first response fails? You'll have to enter the entire form all over again, instead of being able to hit the back button. Madness!
The author's proposal makes things marginally easier for developers and shifts all of the pain to users. That's not really acceptable, at least not to me.
- zkhalique 12y agoForms should probably have a one-time token generated when the form is rendered. This will also help prevent CSRF and DDOS via hogging of resources. In fact, the same technique should apply to session tokens in general. They should be signed by the server, which removes the need to do I/O to filter out unauthorized sessions and mitigate DDOS attacks. So yes, this is the correct way to do it. But additionally, I don't see why it's so bad for the client to prevent quick submissions in succession... maybe disable the button until the response comes back, with a 1 second timeout, and alternate suggestions on repeated failures.
- ryanlol 12y agoSo the DDoS attacker can't retrieve said tokens?
- zkhalique 12y agoWell, presumably creating an account will be expensive, otherwise an attacker can just execute a http://en.wikipedia.org/wiki/Sybil_attack http://en.wikipedia.org/wiki/Sybil_attack Once an account is created, throttling resources is straightforward. If you want, you can allow a person to have one X per Y (time, points, whatever).
- 13 12y agoSigning is slow, if anything signing the CSRF tokens just became your bottleneck, if not you just inflated the request size by kilobytes for no good reason.
- zkhalique 12y agoSigning doesn't require I/O and can be massively parallelized. And also the CSRF token doesn't need signing, it comes from the session data stored on the server. The session id is what needs signing.
- babuskov 12y agoI wish I could upvote this more than once. In the company I worked for for 10+ years (2002-2013) we had a huge web application used heavily by 500+ users daily. I have seen a lot, and the one-time-token approach is the only bulletproof way to solve this once and forever. It's not only the double-click that causes problems. For example, user might click and if the server does not respond right away it might seem that click did not get accepted, so the user will try to click again 3-4 seconds later. The one-time token solves this problem as well. Then you might have AJAX stuff that gives no feedback until the server does its job (which might take a while if it's under load at the time) and if you disable the button it prevents the user to retry. Sure, you can add indeterminate progress bars and animations, but users quickly learn to ignore those if they don't get any response from the server in expected time. What really should be done is educating developers to implement this pattern. Any beginner book that talks about HTML and HTTP should include this. Unfortunately, it's so easy to enter web development today that many people have to learn from their own mistakes, and reinvent the wheel over and over again. Maybe I'm too pessimistic, but I just don't see it happening.
- atomwaffel 12y agoWhile I agree that you should include a one-time token with all sensitive forms, I don't think that disabling links/submit buttons on the first click is a bad idea in general. It's an easy fix that will immediately prevent most accidental redundant server requests, but as it's a client-side fix it's inherently unreliable. What's wrong though is the author's solution of disabling _all_ links and submit buttons on the page indefinitely. Here's an alternative solution that only disables whatever has been clicked on for half a second: (function() { function stopClickEvent (ev) { ev.preventDefault(); ev.stopPropagation(); } document.body.addEventListener('click', function (ev) { if (ev.target.tagName === 'A' || ev.target.getAttribute('type').toLowerCase() === 'submit') { setTimeout(function () { // Needs to happen _after_ the request goes through, hence the timeout ev.target.addEventListener('click', stopClickEvent); }, 0); setTimeout(function () { ev.target.removeEventListener('click', stopClickEvent); }, 500); } }); })(); Unlike the author's solution, this really works for links (they don't support the `disabled` attribute), it doesn't change the appearance of the button, it only disables one element, and it will work for elements that have been added to the document after the DOM was loaded. You can try it here: http://codepen.io/anon/pen/xGKdLX http://codepen.io/anon/pen/xGKdLX
- 13 12y agoThis gets seriously annoying if you have an intermittent connection. I know the first click didn't succeed, so I have the choice of refreshing and losing all my typed content or manually using the web inspector to re-enable the button. It's shitty user experience.
- atomwaffel 12y agoSorry, you might want to re-read my comment. My solution will only disable the button or link that has been clicked on for half a second, which ought to be enough to both prevent accidental and allow intentional resubmissions.
- 13 12y ago
- dheera 12y agoI hate it when people do this. I'd say >30% of my HTTP requests fail on my phone (I have to go into airplane mode and go out of airplane mode about 10 times a day to keep my connection up), so it's really, really annoying when people disable submission because often I do have to try 2-3 times.
- mbell 12y agoThis just moves the concurrency issue to the client, particularly for xhr requests. Say you double click on some action with a one time token, the first request gets to the server and succeeds while the second request fails due to the token being used. But, the response to the first request is delayed and the second response (failure) gets to the client first. The client can either 'obey' this failure response or wait till all in flight requests return (or maybe till one comes through with success). The solutions to dealing with this look a lot like debouncing/throttling the requests in the first place. Using a one time token is very useful for avoiding multiple executions of non idempotent operations but it's really a separate issue from dealing with multiple requests/responses on the client. i.e. the UX.
- scrollaway 12y agoSubsequent requests to the same token don't have to fail - they just have to silently pass.
- mbell 12y agoThat doesn't make the situation any better. Request 1 takes the 'token' Request 2 sees the token gone, returns 'silent success' Request 2 response reaches client, client assumes success. Request 1 fails somewhere internally Request 1 returns failure to client, which has already acted on the success of request 2 User thinks the action was successful, when it wasn't. Now, you can build yourself a lovely two phase commit system around the token and the operations performed by the action to deal with a UI issue, or you can just throttle the requests.
- scrollaway 12y agoIf the first request errored, you can error on the second one as well. I don't see the problem.
- mbell 12y agoIf there is no throttling, there could be a dozen requests, with different responses arriving in arbitrary order. Which one do you believe to determine the outcome of the operation? How long do you wait for responses? etc. etc. If your going to build all the machinery in the client to deal with this situation, it's much easier to just throttle.
- sopooneo 12y agoEven before I got into web dev, I assumed something like that one-time-use token could be implemented to solve the problem. But back in the day, every online store had explicit instructions to "only hit submit once". Why didn't they just use the token solution?
- c22 12y agoBecause back in the day everyone was a novice.
- deleted 12y ago[deleted]