views:

355

answers:

8

I'm building a website using Ubuntu, Apache, and Django. I'd like to block people from filling out and submitting a particular form on my site more than once. I know it's pretty much impossible to block a determined user from changing his IP address, deleting his cookies, and so on; all I'm looking for is something that will deter the casual user from re-submitting.

It seems to me that blocking multiple form submissions from the same IP address is the best way to achieve what I'm looking for. However, I'm unsure how I should do this, and whether I should be doing this from Apache or from Django. Any tips?

Edit: I'm looking to prevent intentional re-submission, not just unintentional double submission. e.g. I have a survey that I want to discourage people from voting multiple times on.

+3  A: 

Several whole countries are NAT'ed, and some (most?) large multinational corporations too, many with several hundred thousand users each. Blocking anything by IP is a bad idea.

Go for a cookie instead, which is as good as it's going to get. You could also make the user login in, in which case you'd know if the form was submitted repeatedly for that login.

krosenvold
Thanks krosenvold. So if a country is NAT'ed, then all visits from that country show the same IP address? Where can I find info on which countries/areas are NAT'ed?
RexE
Well they won't be sharing a *single* IP address, which would probably be too limited. Expect something like a one or more class C networks for a whole country. To my knowledge at least Singapore and Thailand are NAT'ed. I don't know if there is any coordinated source of this information; and things may change too ;)
krosenvold
A: 

I would use the session id, and store form submissions in a table with session id, timestamp, and optionally some sort of form identifier. Then, when a form is submitted, you could check the table to make sure that it had not happened within a certain period of time.

tghw
A: 

Filtering on IP address and/or cookies are both easy to get around, but they will prevent the casual user from accidentally submitting the same stuff multiple times due to browser hick-ups, impatience and so on.

If you want something better than that you could implement login, but of course that prevents a lot of users from responding.

Brian Rasmussen
A: 

Add to the form a monotonically increasing id number in a hidden field.

As each form is submitted, record the id in a "used" list/map (or mark it used, or whatever, implementation detail).

If you get the same id a second time (if it's already in your used map) inform the user they double-submitted.

tpdi
+1  A: 

While nothing is fool proof, I would suggest something like this: When a user loads the page with your form on, a cookie is set and the value of the cookie is appended with a fixed secret string and the md5 value of this is written to a hidden field on the form. Ensure that a new value is generated each time the user access the form.

When the user submits the form, you check that the cookie value and form value match, that the cookie the user was given has not been used to submit the form before and that the referrer id match the URL of the form. Optionally you make sure that there has been no attempts to post from that IP in the last 2 minutes (fast enough that it wont matter to most people, but slow enough to slow down bots).

To fix this the user has to make a script that loads the page, store the cookies and submit the correct values. This is much more difficult than if the user could just submit the form.

Added Based on edit: I would block the users in the Django framework. This allows you to present a much better error message to the user and you only block them from that form.

tomjen
Thanks tomjen for the thoughtful reply. Would I block IP addresses from my Django code, or from Apache?
RexE
+2  A: 

If your main concern is to prevent someone writes a script and automatically submit the form many times, you may want to use CAPTCHA with your form.

Journeyman Programmer
A: 

This is a question of authentication and authorisation, which are related but not the same. In order to manage authorisation you must first authenticate (reliably identify) the user.

If you want to make this resist intentional misuse then you are going to end up with not only usernames and passwords but demands for information that personally identifies your users, along the lines of the stuff a bank asks for when you want to open an account. The bleeding hearts and lefties will snivel endlessly about invasion of privacy but in fact you are doing exactly the same as a bank and for exactly the same reasons.

It's a lot of work and may be affected by law. Do you really want to do it?

Peter Wone
A: 

The following methods are all relatively simple, both to implement and to hack around. Anyone with Firebug and a little knowledge won't even blink.

The following Javascript uses Mootools, and I haven't checked it to be bug free. I understand that JQ syntax is almost identical, and raw JS is similar enough, so the point should be clear.

1) If the form is being submitted via AJAX, you can check before submitting (sorry if I'm just stating the obvious).

var sent = 0;
$('myForm').addEvent('submit', function(){
    if(!sent) this.send(); 
})

This is really simple, and surprisingly effective until they reload the page.

2) Add a javascript cookie. Again, with Mootools:

$('myForm').addEvent('submit', function(){ 
     if(Cookie.read('submitted')){ alert('once only'); return false;}
     else{ Cookie.write('submitted', 1); return true; }
})

This will work even if the user reloads the page.

3) Add a Python session cookie. I am not familiar with Python, but if it is like php, this will have no advantage over method 2. In either case, the user can delete the cookie with FireCookie or WebDeveloper Toolbar (or their equiv's on other browsers) and reload the page.

4) Add a Flash cookie (use Flex). This is ideal - Flash cookies are stored in a different location, are not obvious, and are very difficult to remove. The only downside is that you need to create and embed a tiny swf.

5) Store a value in a hidden field, and check for the value. A hash can be added to the internal links to insure that the value remains filled in even if the page is navigated away from.

6) Other games can be played incrementing a URL (or a custom URL using htaccess) for each visitor.

An swf cookie is the best idea of the above, though it can be combined with the others. Good luck.

SamGoody