Lottery script PHP nog niet veilig.

  • Onderwerp starter Onderwerp starter wuwa
  • Startdatum Startdatum
Status
Niet open voor verdere reacties.

wuwa

Nieuwe gebruiker
Lid geworden
23 mrt 2009
Berichten
4
Hallo,

Ik heb een php script die het mogelijk maakt dat website bezoekers kunnen kijken of hun unieke nummer iets gewonnen heeft.
Er zit alleen nog een groot lek in, en dat is dat andere mensen ook het formulier in kunnen vullen, terwijl ze niet het unieke nummer hebben, kan iemand mij daarbij helpen?

Bezoekers vullen het volgende in:
- Naam
- Postcode
- Huisnummer
- E-mail
- Lotnummer
Deze gegevens worden weggeschreven naar MySQL database,
Het lotnummer bepaald of iemand gewonnen heeft, in het formulier zit een controle of iemand al niet het Lotnummer heeft in gevoerd.

Maar nu komt het want het lek zit hem erin dat welke malle gast ook gewoon wat in het formulier slaat ie dat ook op in het database.

Ik ben bereid het script publiekelijk te maken.

~Wuwa
 
dat is inderdaad het probleem van alle websites :)
Meeste sites gebruiken captchas zodat de bezoeker geen 1000x het gaat invullen.

captchas zijn die afbeeldingen die je moet overtypen en als het hetzelfde is zal het formulier pas worden opgeslagen.

Ik denk zelfs dat php zelf een functie heeft om captchas te maken.
 
ja inderdaad daar zat ik ook aan te denken, maar het is zo, eens een lotnummer ingevuld is hij vergeven.

Het is namelijk zo, in de database zitten reeksen met mogelijke winnende loten, dat is omdat niet iedere lotnummer ingevuld word, want sommige mensen doen niks met hun loten. Het eerste lot wat ingevuld word binnen die reeks is de winnende.

Ik zat zelf ook te denken aan 1x per IP-adres invullen, maarja er zijn ook mensen die wonen met meerdere mensen in 1 huis. Dan kun je misschien wel dat 1x naar 3x zetten zodat je dat al gedeeltelijk afvangt.

Toch blijft het lastig.
 
Ik vind je uitleg nog niet helemaal duidelijk. Ik maak dit er uit op:
  • Mensen mogen zelf hun gegevens invullen en een lotnummer bedenken.
  • Alleen wanneer het lotnummer nog niet in de database staat mag het gekozen worden.
  • De input wordt niet gevalideerd en wanneer er iemand nep informatie opgeeft wordt het lotnummer toch vergeven aan die nep persoon.

(Ik hoop dat ik je vraag een beetje begrijp)

Dat blijft altijd een lastig geval, je kan immers de persoon die de gegevens invoert moeilijk valideren. Om te kijken of het geen robot is wordt er inderdaad gebruik gemaakt van captchas, maar dan ben je er nog niet.
Je kan de input valideren op een bepaalde format, maar ook dat is te omzeilen. Daarom wordt er vaak gebruik gemaakt van een persoonsgebonden validatie als bijvoorbeeld e-mail, telefoonnummer of een lege bank overdracht. De persoon moet middels een van zulke middelen zien te bewijzen dat het om een 'echt' persoon gaat met 'echte' gegevens.
 
Laatst bewerkt:
Je begrijpt het goed, behalve dat ze hun eigen lotnummer mogen bedenken, want die krijgen ze aangerijkt/toegestuurd.

Er worden loten toegestuurd naar de mensen met een uniek nummer, in de reeks van 10000 - 40000. Deze mensen moeten dan hun nummertje op de website invullen met hun adres gegevens. Fake gegevens invullen is heel makkelijk, maar ben ik niet heel bang voor want de loten worden aan 'betrouwbare' deelnemers gegeven. Maar er zal altijd iemand tussen zitten die leuk denkt te doen, en op die manier de hoofdprijs wint.

Hier de code.

PHP:
<?php 

	/*************/
	/*           */
	/*  Prijzen  */
	/*           */
	/*************/

	$prijzenArray = array( 
		"1" => "EEN WEEKEND CENTER PARCS", 
		"2" => "2e prijs",
		"3" => "3e prijs", 
		"4" => "4e prijs", 
		"5" => "5e prijs", 
		"6" => "6e prijs", 
		"7" => "7e prijs", 
		"8" => "8e prijs", 
		"9" => "9e prijs", 
		"10" => "10e prijs",
		"11" => "11e prijs",  
	); 

	/*******************/
	/*                 */
	/*  Einde Prijzen  */
	/*                 */
	/*******************/

	/*******************/
	/*                 */
	/*  MySQL Config   */
	/*                 */
	/*******************/

	$link = mysql_connect('localhost', 'db_gebruikersnaam', 'db_password') or die('Could not connect: ' . mysql_error());
	mysql_select_db('db_naam') or die('Could not select database');
	
	/*******************/
	/*                 */
	/*  Einde Config   */
	/*                 */
	/*******************/

	/**********************/
	/*                    */
	/*  E-mail variablen  */
	/*                    */
	/**********************/

	$mailFrom = "test@gmail.com"; 										// Wat is het e-mailadres waarvan het verstuurd moet worden
	$mailName = "Namens de vereniging"; 								// Namens wie wordt de mail verstuurd
	$mailSubject = "Loting"; 											// Het onderwerp van de mail
	$mailBody = "--- Dit is een automatisch gegenereerd bericht ---"; 	// De inhoud van de mail
	
	/**********************************************************/
	/*                                                        */
	/*  HIERONDER NIET AANPASSEN, tenzij je weet wat je doet  */
	/*                                                        */
	/**********************************************************/

	$mailHeaders = "MIME-Version: 1.0\r\n";
	$mailHeaders .= "Content-type: text/plain; charset=utf-8\r\n";    
	$mailHeaders .= "From: ". $mailName ." <". $mailFrom .">\r\n";
	
	/**********************************************************/
	/*                                                        */
	/*  Einde mail variabelen                                 */
	/*                                                        */
	/**********************************************************/

	/**********************************************************/
	/*                                                        */
	/*  Functie voor het weergeven van "mooie" foutmeldingen  */
	/*                                                        */
	/**********************************************************/

	function errorMsg($string) {
		echo '<div id="errorMsg">'."\n";
		echo '<h3>Oeps...</h3>'."\n";
		echo '<ol>'."\n";
		echo $string;  
		echo '</ol></div>'."\n";
	}

	/****************************************************************/
	/*                                                              */
	/*  Einde Functie voor het weergeven van "mooie" foutmeldingen  */
	/*                                                              */
	/****************************************************************/
	
	/****************************************************************/
	/*                                                              */
	/*  Controle variabelen                                         */
	/*                                                              */
	/****************************************************************/

	$process = false;

?>

<form method="post" class="uniForm">

<?php
  
/* Check of formulier is verzonden */

	if(isset($_POST['submit'])) {
		$process = true;

/* Foutmelding "buffer" */

		$errorMsg = array();

/* eind buffer */

// Controle(s) op naam
	if(empty($_POST['naam'])) {
		$errorMsg[] = 'Je bent vergeten een naam in te vullen.';
	}

// Controle(s) op postcode
	if(empty($_POST['postcode'])) {
		$errorMsg[] = 'Je bent vergeten een postcode op te geven.';
	}

// Controle(s) op huisnummer
	if(empty($_POST['huisnummer'])) {
		$errorMsg[] = 'Je bent vergeten een huisnummer op te geven.';
	} elseif(!is_numeric($_POST['huisnummer'])) {
		$errorMsg[] = 'Het ingevoerde huisnummer is niet geldig.';
	}

// Controle(s) op e-mail
	if(empty($_POST['email'])) {
		$errorMsg[] = 'Je bent vergeten een email adres op te geven.';
		
// Eerst controleren op foute tekens
	} elseif (!preg_match("/^([a-zA-Z0-9])+([\.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-]+)*\.([a-zA-Z]{2,6})$/", $_POST['email'])) {
		$errorMsg[] = 'Het ingevoerde e-mailadres is ongeldig.';
	}

// Controle(s) op code
	if(empty($_POST['getal'])) {
		$errorMsg[] = 'Je bent vergeten het nummer op je flyer in te voeren.';
	} elseif(!is_numeric($_POST['getal'])) {
		$errorMsg[] = 'Het ingevoerde lotnummer is niet geldig.';
	}
 
// Kijken of er fouten zijn
	if(count($errorMsg) > 0) {
		$fullerror = '';
	foreach($errorMsg AS $error) {
		$fullerror.= '<li>'. $error .'</li>'."\n";
	}
	errorMsg($fullerror);
	} else {

// Checken of persoon uniek is
	$checkQuery = "SELECT naam, postcode, huisnummer, email
		FROM deelnemers
		WHERE (LOWER(postcode) ='". strtolower($_POST['postcode']) ."' AND huisnummer = '". $_POST['huisnummer'] ."')
		OR LOWER(naam) = '". strtolower($_POST['naam']) ."'
		OR LOWER(email) = '". strtolower($_POST['email']) ."'
		";
	$checkResult = mysql_query($checkQuery) or die(mysql_error());
		
	if(mysql_num_rows($checkResult) > 0) {
		errormsg('<li>Je mag maar 1x mee doen aan deze actie.</li>');
	} else {
	$deelnemerQuery = "INSERT INTO deelnemers (naam,postcode,huisnummer, email)
		VALUES ('". $_POST['naam'] ."', '". $_POST['postcode'] ."', '". $_POST['huisnummer'] ."', '". $_POST['email'] ."')
		";
	mysql_query($deelnemerQuery) or die(mysql_error());
	$did = mysql_insert_id();

// Kijken of er een prijs op het getal is gevallen
	$lotQuery = "SELECT lid, prijs FROM loten WHERE getal ='". $_POST['getal'] ."'";
	$lotResult = mysql_query($lotQuery) or die(mysql_error());
	if(mysql_num_rows($lotResult) > 0) {

// Er is een prijs -> kijken of die prijs niet al is vergeven
	$lotRow = mysql_fetch_array($lotResult);
	$lid = $lotRow['lid'];
	$prijs = $lotRow['prijs'];
	$winnaarQuery = "SELECT prijs
		FROM winnaars w
		INNER JOIN loten l
		ON w.lid=l.lid
		";
	$winnaarResult = mysql_query($winnaarQuery) or die(mysql_error());
	$winnaarRow = mysql_fetch_array($winnaarResult);
	if($winnaarRow['prijs'] != $prijs) {
	$insertQuery = "INSERT INTO winnaars (lid, did) values ('". $lid ."','". $did ."')";
	mysql_query($insertQuery) or die(mysql_error());
		echo '<div id="succesMsg"><b>Gefeliciteerd, je hebt een: <br /><h3>'. $prijzenArray[$prijs] .'. </h3>gewonnen</b><br /><br />Wij nemen zo snel mogelijk contact met je op! Voor meer info <a href="mailto:info@festival316.nl">mail</a> ons</div>';
			} else {
		errorMsg('...op jouw getal is geen prijs gevallen.');
				}
			} else {
		errorMsg('...op jouw getal is geen prijs gevallen.');
			}
		}
	}
}
?>
HTML:
	<fieldset class="inlineLabels" style="width:470px;">
		<h2>UW GEGEVENS</h2>
		
		<table style="width: 100%" border="0px">
			<tr>
				<td align="right"><label for="naam">Naam</label></td>
				<td><input name="naam" id="naam" value="<?= $process ? $_POST['naam'] : '' ?>" size="25" type="text" class="textInput" /></td>
			</tr>
			<tr>
				<td align="right"><label for="postcode">Postcode</label></td>
				<td><input name="postcode" id="postcode" value="<?= $process ? $_POST['postcode'] : '' ?>" size="25" maxlength="6" type="text" class="textInput" /></td>
			</tr>
			<tr>
				<td align="right"><label for="huisnummer">Huisnummer</label></td>
				<td><input name="huisnummer" id="huisnummer" value="<?= $process ? $_POST['huisnummer'] : '' ?>" size="25" maxlength="4" type="text" class="textInput" /></td>
			</tr>
			<tr>
				<td align="right">E-mail</td>
				<td><input name="email" id="email" value="<?= $process ? $_POST['email'] : '' ?>" size="25" type="text" class="textInput" /></td>
			</tr>
			<tr>
				<td align="right"><label for="getal">Lotnummer</label></td>
				<td><input name="getal" id="getal" value="<?= $process ? $_POST['getal'] : '' ?>" size="25" maxlength="5" type="text" class="textInput" /></td>
			</tr>
			<tr>
				<td align="right">&nbsp;</td>
				<td><button type="submit" name="submit" class="submitButton">Verstuur</button></td>
			</tr>
		</table>
	</fieldset>
</form>
 
Als het via een email word gestuurd kun je een hash meegeven. Simpel voorbeeld is het MD5en van lotnummer+"gflfdtnhcfhjl" (of een andere willekeurige tekenreeks die mensen NIET kennen, maar jij wel).

Dan laat je de mensen de hashcode en het lotnummer invullen. PHP kan dan met dat lotnummer opnieuw de hash vormen (met "gflfdtnhcfhjl" erachter geplakt) en controleren of beide hash waardes hetzelfde zijn.

In principe waterdicht, al is het niet bestand tegen dingen als bruteforcen, al is het onmogelijk om daartegen beveiligd te worden. In combinatie met een captcha of een limiet op het aantal keren invullen per uur (het IP adres word bijvoorbeeld na 10 keer voor een uur geweigerd) is het redelijk veilig.

Denk wel, alles is te omzeilen: Proxies, de geheime tekenreeks achterhalen met rainbowtables. Je maakt het de mensen met behulp van een hash wel erg lastig ;)
 
Kan iemand helpen om stukje PHP hier in te doen die
1. kijkt of IP in database zit.
2. zo niet dan in database zetten met tijd.
3. Wanneer het minder dan een dag geleden is, dan script niet meer uitvoeren, wanneer langer dan 1 dag script wel uitvoeren.

Misschien iemand een begin?

Hiermee krijg je ip-adres te voorschijn.
PHP:
echo $_SERVER['REMOTE_ADDR'];
 
Hangt van je databasestructuur af.

In principe is dit voldoende
Code:
ip_id  INT  AUTO_INC  PRIMARY KEY
adres  VARCHAR(15) UNIQUE
datetime DATETIME

Vervolgens altijd een INSERT-query uitvoeren, bestaat het IP al dan kun je dat in mysql_errno terugvinden en zul je een UPDATE-query uit gaan voeren.

Het checken van de dag kun je denk ik wel met een query oplossen, alleen heb zo geen idee of dat met een SELECT na bovenstaand moet worden gedaan of dat daar een ander mooi trucje voor is met een van de vorige queries.

Wellicht heeft een ander hier wat ideeën over :)
 
Merk op dat er redelijk wat proxies bestaan, en er kan nog steeds iemand een lotnummer intypen die hij eigenlijk niet heeft. IPs een dag blokkeren is een erg zwakke oplossing. Een controle nummer is in dit geval de enige sterke beveiliging.
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan