Page 1 of 2

Voting cheaters who press BACK in the browser. Can I defeat?

Posted: Wed Jan 28, 2004 3:57 pm
by robster
Hi :)

I have FINALLY got my voting system working! It can unfortunately be cheated with the user pressing BACK and doubling their vote. Here is how it works for now:


1) Show voting page
2) user places vote
3) this is done with formdata that loads another page, does the database and php stuff, then re-directs back to the page the user voted from, showing their updated votes.


Now that all works PERFECTLY! :) I love it! The problem is, when the user presses BACK it goes to the page that does the database and php stuff and re-votes as the formdata reloads also.


I will post the code for this page (the one that doest he voting after taking the formdata from the actual vote choice page) and I wonder if anyone can theorise a way to stop that hapening? It'd be really appreciated. SUCH a challenge! :)

Rob

Code: Select all

<?
				require("config.php");
				
				$connection = mysql_connect($dbhost, $dbusername, $dbpassword);
				
				
				require_once ("forum/ipbsdk.php"); // Include SDK Functions and Files
				
				


					//============================================
					//  Get User POST data and assign to variables
					//============================================

					$Submit = trim(stripslashes($_POST['submit']));	
					$MemberId = trim(stripslashes($_POST['MemberId']));	
					$uid = trim(stripslashes($_POST['uid']));
					$entryid = trim(stripslashes($_POST['entryid']));	
					$year = trim(stripslashes($_POST['year']));	
					$month = trim(stripslashes($_POST['month']));	
					$choice = trim(stripslashes($_POST['Choose']));	



					//=======================================================================
					// Covert the $choice variable to something intelligible for the database
					//=======================================================================
					
					if ($choice == "1 - Poor")
					{
						$chosen = "1";
					}
					if ($choice == "2 - Better")
					{
						$chosen = "2";
					}
					if ($choice == "3 - Good")
					{
						$chosen = "3";
					}
					if ($choice == "4 - Very Good")
					{
						$chosen = "4";
					}
					if ($choice == "5 - Excellent")
					{
						$chosen = "5";
					}




					//========================================================================================
					// Let's Actually do the thing.  Let's see if they're logged in, if they've voted etc etc
					//========================================================================================


	//Is the user logged in?
	if (is_loggedin()) {
		
					//Get the logged in users information
					$userinfoid = get_info(); 

					//now find out that users ID
					$member_id = $userinfoid['id']; 
		
		
					//Set x to 0 meaning we presume that memberid has not voted yet
					$x = "0";
		


					//========================
					// Check if user has voted
					//========================
					$votecontent = mysql_db_query($dbname, "SELECT * FROM votes ORDER BY id ASC");
					$Xvotecontent = mysql_fetch_array($votecontent);
					$Max = mysql_num_rows($votecontent);
					for ($loop=1; $loop<=$Max; $loop++)
					{ 
						$VotesEid = $Xvotecontent["eid"];
						$VotesUid = $Xvotecontent["uid"];
						$Votesrating = $Xvotecontent["rating"];
						$VotesMonth = $Xvotecontent["month"];
						$VotesYear = $Xvotecontent["year"];
						
						
						if ($VotesUid == $member_id) //If logged in user HAS voted
							{
							if ($year == $VotesYear) //If user voted in the same year
								{
								if ($month == $VotesMonth)  //If user voted in the same month in the same year
								 	{
									if ($uid == VotesEid)  //If user voted in the same month in the same year on the same Entry
										$x = "1";  //User has voted for this entry
										$printer = "It seems you have already voted for this entry.  You can not vote for an entry more than once..."; //do nothing, they have voted
								 	}
								}
							}


							
				
					$Xvotecontent = mysql_fetch_array($votecontent);	
				   	}//end of for loop
					//===========================
					//End check if user has voted
					//===========================


		
			
			

					//============================================================
					//Now we know what $x is we know if the user has voted or not
					//============================================================
					if ($x == "0")
					{

			
					//==================================
					// Did User Choose a number or not?
					//==================================
					if ($choice != "Choose Rating")
					{
					//Do Database Entry stuff here
					$printer = "You chose to give entry number <b>$entryid</b> a score of <b>$chosen</b>...";
					
					$Add = mysql_db_query ($dbname, "INSERT INTO votes (id,uid,eid,rating,month,year)
  					VALUES ('', $MemberId, $uid, $chosen, $month, '$year')") or die(Mysql_Error()); 
					
					}
					else
					{
					//They have not chosen
					$printer = "Sorry, you did not choose a number from 1 - 5...<br/>REMEMBER, you don't have to vote for EVERY entry, just the ones you want to.";
					}
					
					
					
					}
					//or if the user HAS already rated (ie: x == 1) then tell them they've rated and can't do it again
					else if ($x == "1")
					{
					//do nothing
					}


	//They mustn't be logged in so no, don't stress it... they can't do anything anyway 
	} 
	else 
	{
		$printer =  "You are not logged in!   If you wish to rate this animation please login or register.";
	}


?>






<table width="600" align="center" bordercolor="#333333">
<tr>
<td>
<?
//==============================
// Print the results HERE
//==============================
echo "$printer<br /><br />";
echo "Redirecting you to the voting page in a couple of seconds...<br />";
echo "If you don't get redirected, <a href = "../current_round.php">please click here</a>.<br/>";
//echo "<br><br>Submit - <br> member id - $MemberId<b>$id</b><br>VotesUid = $VotesUid<br>VotesMonth - <b>$VotesMonth</b><br> UID - <b>$uid</b><br>VotesYear - <b>$VotesYear</b><br>year - <b>$year</b><br>month - <b>$month</b><br>Choice - <b>$choice</b>";   


//=======================================================================================
// Redirect back to the voting page so cheaters can't refresh    Sloppy andif they hit BACK they get a double vote (or some other type of wierd vote)
//=======================================================================================

$IE=eregi("MSIE",$HTTP_USER_AGENT); 
if ($IE==true) { 
header("Location: ../current_round.php"); 
exit; 
} 
$NN6=eregi("Gecko",$HTTP_USER_AGENT); 
if ($NN6==true) { 
header("Location: ../current_round.php"); 
exit; 
} 
$NN=eregi("mozilla",$HTTP_USER_AGENT); 
if ($NN==true) { 
header("Location: ../current_round.php"); 
exit; 
} 
else { 
header("Location: ../current_round.php"); 
exit; 
} 



?>

Posted: Wed Jan 28, 2004 4:30 pm
by Straterra
Add a collumn called IP or Username...And store the user's IP and/or Username, and check to see if it has already been used to vote. If so, it doesn't add to the database.

Posted: Wed Jan 28, 2004 5:41 pm
by pickle
That or you could set a session variable in the page that collects the vote. Then, on your voting page, check if that session variable exists. If it does, kick the user out. It might also be helpful to stop caching on that page. I've found that all three of these meta tags are necessary to stop IE from caching.

<meta http-equiv="Cache-Control" Content="no-cache">
<meta http-equiv="Pragma" Content="no-cache">
<meta http-equiv="Expires" Content="0">


Hopefully this helps.

Posted: Wed Jan 28, 2004 6:10 pm
by Straterra
But then, all someone has to do is close their browser, then come back onto the site.

Posted: Wed Jan 28, 2004 8:07 pm
by timvw
If you make sure your users have to login first, you could store which users have voted. But then they can sign-up X times...

If you look at the ip of your voters, you run into trouble when they are behind a NAT.

If you send them a cookie, they can delete the cookie

In other words: There is no optimal way to do it.

Posted: Wed Jan 28, 2004 10:58 pm
by xisle
track by email address by requiring login and set 2 cookies -- you cannot eliminate the problem but you can discourage it

Posted: Thu Jan 29, 2004 2:09 pm
by robster
Thanks for the replies everone, some thoughful responses :) I love a good discussion like that!

My Users DO have to login first and I do have whether or not they have voted in the votes table. (see my code below for the check).

What confuses me to an extent I guess is that I would have thought this code that is in place would have caught such a thing. Can somebody theorise as to why it hasn't? it's sort of beyond me as I thought that removing the cache state (using the last suggestion) would have fixed it but it still hasn't.

Code: Select all

 if ($VotesUid == $member_id) //If logged in user HAS voted 
                     { 
                     if ($year == $VotesYear) //If user voted in the same year 
                        { 
                        if ($month == $VotesMonth)  //If user voted in the same month in the same year 
                            { 
                           if ($uid == VotesEid)  //If user voted in the same month in the same year on the same Entry 
                              $x = "1";  //User has voted for this entry 
                              $printer = "It seems you have already voted for this entry.  You can not vote for an entry more than once..."; //do nothing, they have voted 
                            } 
                        } 
                     }
Now cookies are definately something I'm going to play with, but why wouldn't that code work, even if it WAS refreshed to run twice? Surely the check would have made that impossible?

Thanks a load! :)

Rob

Posted: Thu Jan 29, 2004 2:30 pm
by pickle
Maybe put that logic in the same location as the query is done. I think with the way it's set up, the code you 've shown just changes the way the page is displayed. However, that will not have any effect if the user just clicks the back arrow. Putting this logic around the query will make sure, each time a vote is submitted, that the vote is valid.

Posted: Thu Jan 29, 2004 2:33 pm
by robster
erm, thanks for your reply and please forgive my ignorance... but I thought my code was where my query is. If you scroll up and see the whole code, I need to have it at least AFTER where it's at so I can get the data from the database in the loop.

Again, I may be miss-understanding you.

Thanks again so much though! :)

Rob

Posted: Thu Jan 29, 2004 2:39 pm
by pickle
Yes, my apologies for not being clearer. The query I was referring to was the query that actually put the vote in the database, not the query that determines if the user is eligible. That way, the only way for the insertion to succeed is if it's just been determined that the vote is allowable.

Posted: Thu Jan 29, 2004 4:30 pm
by robster
Alright! :)

Your logic is sound. I know it's going to work. Here is my prediciment though, it's SO wierd!

Imagine this piece of code:

Code: Select all

// Let's find out what the variable are before doing the query
$printer2 = "member_id is $member_id and VotesUid = $VotesUid";

//Now we know what they are, and they are printed to screen elsewhere, lets see if the IF query works, now this is wierd!
if ($VotesUid == $member_id) //If logged in user HAS voted
   {
       //print the member_id variable
	$printer2 = "member_id is $member_id";
   }
   else 
	{
       //print the error message
	$printer = "Sorry, Your member_id has already voted";
	}

OK, so according to the above logic:
If $VotesUid is the same as $member_id then output what $member_id is.
Else Print the error message.

Now this is just a cut down segment i've adjusted so we can focus on the problem, but what is happening is the code is printing Sorry, Your member_id has already voted AND it's after getting the stuff from the database I know from the code at the top that VotesUid = 999999 and member_id == 1.

So, if VotesUid and member_id are DIFFERENT, as they clearly are, how can the code print the error message when the error message should only appear when the else statement kicks in?!

Also, to top that, in the FULL code, the $printer2 = "member_id is $member_id"; is not activated or used meaning the IF loop doesn't get used, rather, it's the ELSE loop that gets used! how wierd is THAT!

:)

Crazy.


Can anyone see my glaring mistake? or is there a bug here?

Thanks


Rob

Posted: Thu Jan 29, 2004 4:41 pm
by robster
actually, that may not be TOTALLY accurate as there ARE embedded IF statements in the real code, so here is the real code if you want to wade thru it... Is too strange for my brain :)

Code: Select all

$votecontent = mysql_db_query($dbname, "SELECT * FROM votes ORDER BY id ASC");
					$Xvotecontent = mysql_fetch_array($votecontent);
					$Max = mysql_num_rows($votecontent);
					for ($loop=1; $loop<=$Max; $loop++)
					{ 
						$VotesEid = $Xvotecontent["eid"];
						$VotesUid = $Xvotecontent["uid"];
						$Votesrating = $Xvotecontent["rating"];
						$VotesMonth = $Xvotecontent["month"];
						$VotesYear = $Xvotecontent["year"];

						$printer2 = "member_id is $member_id and Votes_Uid = $VotesUid";	
						if ($VotesUid == $member_id) //If logged in user HAS voted
							{
							$printer2 = "member_id is $member_id";
							if ($year == $VotesYear) //If user voted in the same year
								{
								if ($month == $VotesMonth)  //If user voted in the same month in the same year
								 	{
									if ($uid == VotesEid)  //If user voted in the same month in the same year on the same Entry
										$Add = mysql_db_query ($dbname, "INSERT INTO votes (id,uid,eid,rating,month,year)
					  					VALUES ('', $MemberId, $uid, $chosen, $month, '$year')") or die(Mysql_Error()); 
										$printer = "You chose to give entry number <b>$entryid</b> a score of <b>$chosen</b>... Data entered into database, your vote has been placed!"; //Tell em they have voted
										$x = "1";  //User has now voted... they cannot vote again.
								 	}
									else 
									{
									$printer = "Sorry, You have already voted in the same month, year and on this entry";
									}
									
									
									
								}
								else 
								{
								$printer = "Sorry, You have already voted in the same year";
								}
								
								
							}
							else 
							{
							$printer = "Sorry, Your member_id has already voted";
							}
							
							
							
					$Xvotecontent = mysql_fetch_array($votecontent);	
				   	}//end of for loop

Posted: Thu Jan 29, 2004 4:51 pm
by pickle
When I'm about to snap at the world, I usually try to validate my rage by echoing variables in places where I THINK I know what their values should be. In your case, try echoing $VotesEid, $VotesUid, and all the other $Votes variables, as well as the member_id variable, right before you start going into those if statements. I can almost guarantee you that those values aren't going to be what you thought they were. Note the "almost".

Posted: Thu Jan 29, 2004 4:56 pm
by robster
I "think" this is where you can see your "almost" comes into play ;)


Note in the code in my last past that I actually do check the values of the variables before doing the IF statement.

I have:

Code: Select all

                  $printer2 = "member_id is $member_id and Votes_Uid = $VotesUid";    
                  if ($VotesUid == $member_id) //If logged in user HAS voted 
                     {
It does print different numbers, but, it also doesn't go into the loop and it prints the error message that is associated with that IF not being processed which is Sorry, Your member_id has already voted.

How's THAT for baking your noodle! ;)
(thanks again :))

Rob

Posted: Thu Jan 29, 2004 4:58 pm
by robster
erm... hold on... I *cough* just realised that I was doing an IF they are the SAME, then go ahead... obviously they are different and they won't go ahead.... PLEASE excuse me whilst I quickly exit, re-code, test and come back with a full explination of the error of my ways.

(rob exits stage left, to return soon)

SORRY!

(too funny)