Menu

Securitate Web: Cod Captcha

In vremurile noastre programarea este foarte accesibila, atat de accesibila incat dupa 7 zile(fara sa fii avut contact cu limbaje de programare niciodata) in PHP ti-ai construit propriul bot ce poate stresa administratorii in cel mai cumplit mod.
Vad tot mai des formulare de contact neprotejate prin nimic, fara cod captcha, sau si mai rau iti baga o adunare vizibila din sursa. Pai serios vorbind acum, el ca programator web, nu stie ca un bot lucreaza cu sursa? Neatentia programatorilor web la securitate este jignitoare, scuza lor intotdeauna este ca „Nimeni o sa faca un bot special pentru site-ul meu”. Ei pe naiba, sa-mi dea un mail ca il fac eu, dar daunele suportate de ei.

In acest tutorial vom invata despre captcha-uri, nu articole de 3 randuri unde prezint ce este un captcha si afisez 3 linii de cod dupa plec sa ma culc, un tutorial amanuntit unde vom experimenta mai multe metode de captcha.

Ce este un captcha?

Un cod captcha este un cod cu caractere random(alese la intamplare) obligatoriu introduse intr-o imagine pentru a testa daca vizitatorul este un bot sau un vizitator uman.
Cu cat un captcha este mai complicat, mai intors, mai impodobit, cu atat mai imposibila este spargerea lui. Chiar si un captcha simplu, un text drept cu un font default pe o imagine alba este foarte greu de spart, algoritmii pentru OCR fiind foarte complicati.

Cum folosim captcha in PHP?

  • Un fisier PHP genereaza un cod captcha, il salveaza intr-o sesiune, iar apoi il afiseaza ca imagine
  • Vizitatorului ii este afisat codul captcha si un input unde sa introduca ce caractere vede in imagine
  • Datele sunt trimise prin POST sau GET la alta pagina(cand vizitatorul apasa pe submit sa introduca comentariul de exemplu)
  • Acea pagina verifica ce a trimis el prin POST sau GET cu ce este in sesiune
  • In caz ca codul nu a fost introdus corect il anunta ca a gresit si inchide scriptul(exit)

Partea cea mai complicata, crearea imaginii, va fi subiectul principal al acestui articol. Dar si celelalte vor fi explicate.
Pentru intelege acest articol ai nevoie deja sa intelegi lectia Generare imagini cu PHP

Introducere | Captcha Simplu

In prima parte vom crea un captcha foarte simplu.

Structura fisierelor

Structura fisierelor este urmatoarea: Nu uitati sa downloadati fontul, click pe el sa-l downloadati.

Crearea imaginii

Cream un fisier PHP pe server numit captcha.php, in acel fisier va generat codul, generata sesiunea si generata imaginea.
Cod captcha.php:
<?php  
session_start();

$image_width 	= 200;
$image_height = 60;
$ih 	= imagecreate($image_width, $image_height);

$negru 	= imagecolorallocate($ih, 0, 0, 0); // background
$rosu 	= imagecolorallocate($ih, 255, 0, 0); // text

imagefill($ih, 0, 0, $negru); // umplem backgroundul cu negru

$cod_captcha = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 5);
$_SESSION['cod'] = $cod_captcha; // salvam codul in sesiune

putenv('GDFONTPATH=' . realpath('fonts/')); // setam folderul pentru fonturi
$font = 'KiteOne';
# Cu coords centram text-ul.
$coords = imagettfbbox(29, 0, $font, $cod_captcha); //  Coordonate pentru a centra textul
$start_x = ($image_width - $coords[2])/2;
imagettftext($ih, 29, 0, $start_x, 40, $rosu, $font, $cod_captcha);

header('Content-type: image/png');
imagepng($ih);
?>

Pagina unde afisam captcha

Noi aici vom da ca exemplu o pagina de contact, unde cand dai click pe imagine iti schimba codul captcha primit(pentru asta vom folosii JavaScript).

Cod contact.html:
<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Contacteaza-ma</title>
</head>
<body>
	<form action="submit-contact.php" method="POST">
		<table cellpadding="5" border="3" style="width:50%; margin:0 auto;">
			<tr>
				<td>Subiect</td>
				<td><input type="text" name="subiect"></td>
			</tr>
			<tr>
				<td>Mesaj</td>
				<td> <textarea name="mesaj" style="width:90%;" rows="3"></textarea> </td>
			</tr>
			<tr>
				<td>Captcha</td>
				<td>
					<img src="captcha.php" id="captcha" onclick="this.src='captcha.php?'+new Date().getTime()" alt="">
					<br><sub>* Scrieti textul din imagine in casuta de mai jos</sub><br>
					<input type="text" name="captcha">
				</td>
			</tr>
		</table>
		<input type="submit" value="Trimite mesaj" style="margin-right:25%; float:right;"/>
		
	</form>
</body>
</html>
Folosim la javascript in adresa url si time-ul curent ca un bypass pentru "cache-ul" care se face la imagini.

Validarea captcha-ului

In aceasta etapa am primit codul captcha din formular si trebuie sa vedem daca este corect.

Cod submit-contact.php:
<?php  
session_start();
if($_SERVER['REQUEST_METHOD'] != 'POST'){
	// Metoda de REQUEST nu este POST
	header('Location: contact.html'); exit;
}

if(isset($_SESSION['captcha'])){
	if($_POST['captcha'] == $_SESSION['captcha']){
		// Aici am trimite emailul dar este doar un exemplu
		echo 'Mail trimis cu success!';
	} else{
		echo 'Codul captcha este gresit! Veti fi redirectionat inapoi in 3 secunde.';
		header('Refresh: 3; url=contact.html'); exit;
	}
	unset($_SESSION['captcha']); // stergem captcha-ul
} else{
	echo 'Captcha-ul nu a fost primit! Veti fi redirectionat inapoi in 3 secunde.';
	header('Refresh: 3; url=contact.html'); exit;
}

?>

Demo

Aveti un demo cu codurile de mai sus in actiune aici

Securizare Imagine captcha

In aceasta parte lucram doar cu fisierul captcha.php, structura ramane aceeasi cu cu cea de la Introducere | Captcha Simplu dar poate suferii mici modificari pe parcurs.

Font aleatoriu

O buna practica este folosirea fonturilor extravagante, cat mai ciudate, dar o si mai buna practica este folosirea unui font aleatoriu(adica dintr-o lista de fonturi alegeti unul la intamplare cu php la fiecare vizita), sa complicam munca bot-ului. Insa nu este bine sa exagerati, sa folositi maxim 10 fonturi. In primul va fi necesar sa downloadati fonturile de aici si sa puneti toate fonturile in folder-ul fonts.

Structura fisiere noua

Downloadati fonturile din arhiva aceasta nu pierdeti timpul luandu-le pe rand.

Cod captcha.php:
<?php  
session_start();

$image_width 	= 200;
$image_height 	= 60;
$ih 	= imagecreate($image_width, $image_height);

$negru 	= imagecolorallocate($ih, 0, 0, 0); // background
$rosu 	= imagecolorallocate($ih, 255, 0, 0); // text

imagefill($ih, 0, 0, $negru);

$cod_captcha = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 5);
$_SESSION['captcha'] = $cod_captcha; // salvam codul in sesiune

putenv('GDFONTPATH=' . realpath('fonts/')); // setam folderul pentru fonturi
$fonturi = array(
		'Playball',
		'JustMeAgainDownHere',
		'Englebert',
		'Yesteryear',
		'KiteOne',
		'Spirax',
		'Nosifer',
		'JollyLodger',
		'Davonshire',
		'Finger Paint'
	);
$font 	= $fonturi[ rand(0, count($fonturi)-1) ]; // alegem un font random
$coords = imagettfbbox(29, 0, $font, $cod_captcha); //  Coordonate pentru a centra textul
$start_x = ($image_width - $coords[2])/2;
imagettftext($ih, 29, 0, $start_x, 40, $rosu, $font, $cod_captcha);


header('Content-type: image/png');
imagepng($ih);
?>


Deoarece fonturile sunt foarte periculoase cand vine vorba de majuscule sau minuscule trebuie sa lasam vizitatorul sa le bage cum doreste, deci schimbam putin validarea.
Cod submit-contact.php:
<?php  
session_start();
if($_SERVER['REQUEST_METHOD'] != 'POST'){
	// Metoda de REQUEST nu este POST
	header('Location: contact.html'); exit;
}

if(isset($_SESSION['captcha'])){
	if(strtolower($_POST['captcha']) == strtolower($_SESSION['captcha'])){
		// Aici am trimite emailul dar este doar un exemplu
		echo 'Mail trimis cu success!';
	} else{
		echo 'Codul captcha este gresit! Veti fi redirectionat inapoi in 3 secunde.';
		header('Refresh: 3; url=contact.html'); exit;
	}
	unset($_SESSION['captcha']); // stergem captcha-ul
} else{
	echo 'Captcha-ul nu a fost primit! Veti fi redirectionat inapoi in 3 secunde.';
	header('Refresh: 3; url=contact.html'); exit;
}
?>

Un demo aveti pe pagina aceasta

Linii

Inca o buna strategie mai este sa adaugati linii in captcha. Ganditi-va logic, linii cu aceeasi culoare ca textul, in numar de ~20, vor incurca complet un algoritm slab facut.

Cod captcha.php:
<?php  
session_start();

$image_width  = 200;
$image_height = 60;
$ih 	= imagecreate($image_width, $image_height);

$negru 	= imagecolorallocate($ih, 0, 0, 0); // background
$rosu 	= imagecolorallocate($ih, 255, 0, 0); // text

imagefill($ih, 0, 0, $negru);

$cod_captcha = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 5);
$_SESSION['captcha'] = $cod_captcha; // salvam codul in sesiune

putenv('GDFONTPATH=' . realpath('fonts/')); // setam folderul pentru fonturi
$font 	= 'KiteOne';

$coords = imagettfbbox(29, 0, $font, $cod_captcha); //  Coordonate pentru a centra textul
$start_x = ($image_width - $coords[2])/2;
imagettftext($ih, 29, 0, $start_x, 40, $rosu, $font, $cod_captcha);

# Urmeaza sa cream un numar intre 5 si 15 de linii cu acceasi culoare ca textul
$linii 	= rand(5,15);
for ($i=1; $i < $linii; $i++) { 
	imageline($ih, rand(1,$image_width) , rand(1,$image_height), rand(1,$image_width) , rand(1,$image_height), $rosu);
}

header('Content-type: image/png');
imagepng($ih);
?>

Un demo la pagina aceasta

Puncte

Aceasta practica nu am mai vazut-o prin alte parti, dar:
  • 1. Imi place cum arata
  • 2. Nu incurca
  • 3. Nu poate face nimic rau, decat bine


Cod captcha.php:
<?php  
session_start();

$image_width 	= 200;
$image_height = 60;
$ih 	= imagecreate($image_width, $image_height);

$negru 	= imagecolorallocate($ih, 0, 0, 0); // background
$rosu 	= imagecolorallocate($ih, 255, 0, 0); // text

imagefill($ih, 0, 0, $negru);

$cod_captcha = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 5);
$_SESSION['captcha'] = $cod_captcha; // salvam codul in sesiune

putenv('GDFONTPATH=' . realpath('fonts/')); // setam folderul pentru fonturi
$font 	= 'KiteOne';

$coords = imagettfbbox(29, 0, $font, $cod_captcha); //  Coordonate pentru a centra textul
$start_x = ($image_width - $coords[2])/2;
imagettftext($ih, 29, 0, $start_x, 40, $rosu, $font, $cod_captcha);

$linii 	= rand(5,15);
for ($i=1; $i < $linii; $i++) { 
	imageline($ih, rand(1,$image_width) , rand(1,$image_height), rand(1,$image_width) , rand(1,$image_height), $rosu);
}

# Adaugam 200 de puncte
for ($j=0; $j < 200; $j++) { 
	$x 	= rand(1,$image_width);
	$y 	= rand(1,$image_height);
	# Folosesc imageline pentru a desena un punct deoarece are aceleasi coordonate de pornire ca de oprire
	imageline($ih,  $x, $y, $x , $y, $rosu);
}

header('Content-type: image/png');
imagepng($ih);
?>
Demo la pagina aceasta


Ti-a placut articolul? Asigura-te ca-ti dam de veste cand publicam altele noi.


Tags: Tutoriale, Programare Web, Php

Comments Nota 5 din 3
marian kortis
marian kortis
Daca se poate as vrea sa imi trimita pe aceasta adresa cel ce a scris cele de mai sus pe adresa de email [email protected] instructiuni despre cum se creaza un bot