I am trying to create a web survey questionaire, but I don't want same people using same ip to submit it more than once within an hour time, I am using php, I assume I need to use
$_SERVER['REMOTE_ADDR']
to get the client ip and store in the session or database and then use it to compare the new ip, I am not sure if it is right and don't know how to exact implement this in php, can anyone help me with it, thanks in advance!
-
3Similar: http://stackoverflow.com/questions/8260916/prevent-multiple-registrations-on-a-survey-website. – GG. Dec 02 '11 at 12:33
-
2That's the correct basic approach, yes. But that requirement is very broken. – derobert Dec 02 '11 at 12:33
-
You'll also need a database table to keep track of the IP's and submission time. Then use php to compare these. – Nick Zinger Dec 02 '11 at 12:35
-
possible duplicate of [Stopping people from hijacking a voting system using PHP?](http://stackoverflow.com/questions/2165828/stopping-people-from-hijacking-a-voting-system-using-php) – mario Dec 02 '11 at 12:42
5 Answers
When survey is submitted:
/*
Tracking table structure:
`id` INT(11) unsigned NOT NULL AUTO_INCREMENT
`client_ip` VARCHAR(15) NOT NULL
`submitted_time` DATETIME NOT NULL
*/
$query = "SELECT count(`id`) AS 'count'
FROM `tracking_table`
WHERE
`client_ip` = '".mysqli_real_escape_string($link, $_SERVER['REMOTE_ADDR'])."'
AND `submitted_time` > '".date('Y-m-d H:i:s',strtotime('-1 hour'))."'
LIMIT 1";
$result = mysqli_fetch_assoc(mysqli_query($link, $query));
if ($result['count'] > 0) {
echo "You have already submitted within the last hour";
exit;
}
// process survey here
$query = "INSERT INTO `tracking_table`
(`client_ip`, `submitted_time`)
VALUES
('".mysqli_real_escape_string($link, $_SERVER['REMOTE_ADDR'])."', ".date('Y-m-d H:i:s').")";
mysqli_query($link, $query);
However, you may find that this is not a good requirement - there are many valid situations where multiple users may use the same IP address (such as, for example, student accomodation). You may be preventing valid submissions by imposing this limit.
EDIT
Here is a basic outline of how you might do this with cookies (taking into account the limitations discussed below).
Our cookie system works on a pair of values. ckv_1
will hold the timestamp at which the last survey was submitted. ckv_2
will hold a hash based on the timestamp and a salt, in an effort to stop people from screwing with the cookies. Obviously, if both cookies are deleted, we won't be able to detect it, but at least this provides some sort of validation:
function get_cookie_hash ($timestamp, $salt) {
return md5("Extra random static string; TS: $timestamp; Salt: $salt; Extra random static string.");
}
$cookieSalt = 'Th1si54rAnd0MsTr1nG!';
// If at least one of the cookies was returned, validate the request
if (!empty($_COOKIE['ckv_1']) || !empty($_COOKIE['ckv_2'])) {
$valid = FALSE;
do { // Wrapped in a do-while to allow us to break out easily
// Make sure both value are set
if (empty($_COOKIE['ckv_1']) || empty($_COOKIE['ckv_2'])) break;
// Get old timestamp as integer
$oldTS = hexdec($_COOKIE['ckv_1']);
// Make sure timestamp is more than one hour old, and the hash cookie matches it
if ($oldTS > (time() - 3600) || $_COOKIE['ckv_2'] != get_cookie_hash($oldTS, $cookieSalt)) break;
// OK if you get here
$valid = TRUE;
} while (FALSE);
if (!$valid) {
echo "Sorry - you cannot submit a survey more than once in an hour.";
exit;
}
}
// process survey here
// Set the tracking cookies after processing (but before any output!)
// We'll set them as HTTP only to help prevent XSS-type attacks
$cookieTime = time();
setcookie('ckv_1', dechex($cookieTime), 7200, '', '', FALSE, TRUE);
setcookie('ckv_2', get_cookie_hash($cookieTime, $cookieSalt), 7200, '', '', FALSE, TRUE);

- 87,921
- 11
- 154
- 174
-
-
@hakre best approach would (I think) be to convert IPv4 to IPv6 and store everything as v6, but I don't have a handy function for this... – DaveRandom Dec 02 '11 at 12:49
-
Good idea, this should be possible. But isn't it easy to fool the script then? I mean there are so many ipv6 addresses. – hakre Dec 02 '11 at 12:53
-
@hakre There is a standard agreed way to do it, and IPv6 addresses *shouldn't* (although that's not to say they won't) be issued that clash with the IPv4 converted addresses. Right now, I can't find a reference of how you're supposed to do the conversion, but I know I read somewhere official that there is a way to do it. [This page](http://www.fnode.com/2009/11/convert-ipv4-into-ipv6/) illustrates (not that well) how it is done - but I can't find the "official" statement on it. – DaveRandom Dec 02 '11 at 13:48
-
@Dave, good point, so how to solve that issue if the students in accoummodation share the same ip, is there way I can do to limit the particular pc instead of ip? – smith Dec 02 '11 at 16:27
-
@smith Maybe cookies? Although this is easy to fool as it is completely client side. I can't think of a 100% foolproof way to handle this, unfortunately. – DaveRandom Dec 02 '11 at 16:33
-
@Dave thank you, is there a way I can keep track the location? but as you said students in the accommodation share the same ip as they will share the same location right? – smith Dec 02 '11 at 16:45
-
It can be generally assumed that ip = location (for this exercise at least). The cookies approach is probably as good as you will get, this would limit the submissions from a particular browser on a particular PC. But as I said, this can easily be fooled by anyone who knows a little bit about how this works. – DaveRandom Dec 02 '11 at 16:56
-
so what I need to store in php cookies, user's unique email address or other authentications – smith Dec 02 '11 at 18:30
-
@Dave, I think if I store the cookie on the server side, it shouldn't be deleted even the user delete the cookie on the brower, because that's located on the client side. – smith Dec 02 '11 at 19:05
-
@smith Cookies are stored client-side - you cannot (usefully) store on the server because if they are deleted from the client, you will not be able to associate the client with the correct cookie values. That's sort of the point of cookies - they are client-specific data. – DaveRandom Dec 02 '11 at 19:48
-
@Dave thanks a lot, I think I might do cookies and ip together so to get a combination of both, do I need to set cookies and insert ip on the point in which the form is successfully submitted? – smith Dec 02 '11 at 23:35
-
@smith There is not a lot of point in combining cookies with IP, it doesn;t afford any extra benefit, because the two work completely independently of each other. See edit above for example of cookie approach. – DaveRandom Dec 03 '11 at 14:15
-
@Dave, thanks for your edit, but I think in the do while loop , it should be $oldTS < (time() - 3600) instead of $oldTS > (time() - 3600), and why $_COOKIE['ckv_2'] != get_cookie_hash($oldTS, $cookieSalt), are you comparing current cookie with old cookie? – smith Dec 05 '11 at 15:31
-
@smith you are correct about the `>` being wrong, that's a typo. I added the part where the cookie is compared with an old value to make it so that the cookies both required and must match in order to be valid. This means that if someone tries to modify one of the cookies to circumvent the system, they will be blocked. Although as I say, if they just delete both of them it will be easy to get around... – DaveRandom Dec 05 '11 at 15:40
-
@smith actually, now I think about it, `>` is correct, because it means it will not validate if the old timestamp is more recent than one hour ago. – DaveRandom Dec 05 '11 at 15:41
Use a database to store IPs and timestamps of votes, and then when recording the vote (or better yet; when displaying the survey so you tell the user that he already voted) check the database if user's IP ($_SERVER['REMOTE_ADDR']) is already in the DB and if the timestamp is younger than one hour. If it is don't allow him to vote, otherwise do.

- 53,269
- 16
- 95
- 99
You can wrap your checks in a class and then use it when your action requires the functionality:
class IPChecker
{
public function storeIP($ip)
{
# save $ip with now() and context (if available)
# in your database
...
}
public function isBlocked($ip)
{
# test if $ip by now() and context (if available)
# is in your database
...
# return true / false;
}
}
$ipchecker = new IPChecker();
# on form submit:
$ip = $_SERVER['REMOTE_ADDR'];
if ($ipchecker->isBlocked($ip))
{
# blocked
...
}
else
{
# good
$ipchecker->storeIP($ip);
...
}

- 193,403
- 52
- 435
- 836
REMOTE_ADDR
does indeed get you an IP address. But:
- At many companies (especially large ones), outgoing traffic goes through proxies or firewalls, which makes the entire company—or at least entire location—appear to come from a few IP addresses. One IP address may easily be 10,000 people.
- Some ISPs use transparent proxying to save on bandwidth. Often, its only transparent to the client, so an entire ISP (or at least region) will come from several IPs.
- Cell phones often are assigned new IP addresses more often than every hour, and being told you've already voted when you haven't would be quite annoying.
- IPv6 opens a whole new can of worms. Privacy extensions are designed to break what you're doing. They will.
So, here is a more accurate description of REMOTE_ADDR
: An address that, as part of the full TCP session address (server, server port, remote address, remote port) lets you send a packet that is part of said session. It may or may not be the address of the actual client (usually isn't), may or may not match from request-to-request, and may or may not be shared by numerous other people.

- 49,731
- 15
- 94
- 124
Store the $_SERVER['REMOTE_ADDR']
in database table with the time stamp it last submitted the survey. The table may have two columns like IPAddress(varchar 100), TimeStamp(int)
. and in php code
<?php
//query the $_SERVER['REMOTE_ADDR'] in database and get timestamp, if found compare it with current time stamp, difference should be greater than an hour
if($diff > 3600)
{
print "You are not allowed to post your survey more than once in an hour"
exit;
}
//Your page code
?>

- 3,541
- 6
- 40
- 71