Ghidul Securitatii PHP: Sesiuni
< InapoiInainte >Shared HostsBaze de date si SQL
Session Fixation
Securitatea sesiunii este un subiect complicat, si nu este o surpriza ca sesiunile sunt frecvent o tinta a atacurilor. Atacatorul este in general un impostor, adica are acces la sesiunea unui utilizator (legitim) facand aplicatia sa creada ca este acel utilizator.
Cea mai importanta informatie pentru atacator este session identifier-ul, pentru ca este nevoie de acesta pentru a lansa un atac "impostor". Exista trei metode folosite in mod curent pentru a obtine aceasta informatie:
Prezicere
Capturare
Fixation
Prezicerea inseamna ghicirea valorii identifier-ului sesiunii. Cu mecanismul PHP-ului pentru sesiuni, identifier-ul este ales la intamplare, si este putin probabil ca acesta sa fie veriga cea mai slaba a aplicatiei.
Capturarea unui session identifier valid este cea mai frecventa metoda de atac, si exista mai multe posibilitati de a face acest lucru. Deoarece session identifier-urile sunt plasate in mod normal in cookies sau ca variabile GET, posibilitatile se refera la modalitati de atacare a acestor metode de transfer. Desi au existat cateva vulnerabilitati ale browserurilor in ceea ce priveste cookies, mai ales in Internet Explorer, cookies sunt mai putin expuse decat variabilele GET. Astfel, pentru utilizatorii care permit cookies, poti crea un mecanism pentru propagarea session identifier-ului folosind cookies.
Fixation este cea mai simpla metoda de a obtine un session identifier valid. Nu este greu sa-ti protejezi aplicatia de acest tip de atac. Totusi, daca mecanismul sesiunii consta in session_start() si nimic mai mult, aplicatia este vulnerabila.
Pentru a demonstra session fixation, voi folosi urmatorul script, session.php:
<?php
session_start();
if (!isset($_SESSION['visits']))
{
$_SESSION['visits'] = 1;
}
else
{
$_SESSION['visits']++;
}
echo $_SESSION['visits'];
?>
La prima vizita ar trebui sa vezi 1 printat pe ecran. La fiecare vizita ulterioara, acesta ar trebui sa creasca, reflectand de cate ori ai vizitat pagina.
Pentru a demonstra atacul, intai asigura-te ca nu ai un session identifier (poate stergand cookies), apoi viziteaza pagina cu ?PHPSESSID=1234 adaugat la URI. Apoi, cu un browser diferit (sau cu alt computer), viziteaza pagina cu ?PHPSESSID=1234 adaugat la URI. Vei observa ca pe pagina nu scrie 1 la prima vizita, ci numaratoarea continua de unde a ramas la sesiunea precedenta.
De ce acest lucru poate prezenta probleme? Majoritatea atacatorilor folosesc un link sau un redirect la nivel de protocol pentru a trimite un utilizator la un alt site cu session identifier-ul adaugat la URI. Utilizatorul poate sa nu observe, deoarece site-ul va arata la fel. Deoarece atacatorul a ales session identifier-ul, acesta este deja stiut si poate fi folosit pentru a lansa atacuri "impostor" cum ar fi session hijacking.
Un atac simplistic ca acesta este usor de prevenit. Daca nu exista o sesiune activa asociata cu session identifier-ul pe care user-ul il prezinta, atunci regenereaza-l pentru a fi sigur:
<?php
session_start();
if (!isset($_SESSION['initiated']))
{
session_regenerate_id();
$_SESSION['initiated'] = true;
}
?>
Problema cu o aparare atat de simplista este ca un atacator poate initializa o sesiune pentru un anumit session identifier, apoi poate folosi session identifier-ul pentru a lansa atacul.
Penru a te proteja impotriva acestui timp de atac, trebuie sa observam ca session hijacking este folositor doar daca utilizatorul are anumite privilegii (spre exemplu este logged in). Deci, daca regeneram session identifier-ul oricand are loc o schimbare in privilegii (spre exemplu, dupa verificarea username-ului si a parolei) am eliminat riscul unui atac de acest gen.
Deturnarea Sesiunii
Probabil cel mai comun tip de atac al sesiunii, deturnarea sesiunii se refera la toate atacurile care incearca sa acceseze sesiunea unui alt utilizator.
Ca si cu session fixation, daca mecanismul sesiunii consista doar in session_start(), esti vulnerabil, desi atacatorului nu ii va fi la fel de usor.
In loc sa ma axez pe prevenirea capturarii session identifier-ului, voi explica cum sa faci o astfel de capturare mai putin periculoasa. Scopul este sa faci impersonarea unui utilizator de catre atacator cat mai complicata, deoarece fiecare complicatie creste nivelul de securitate. Pentru a face acest lucru, sa examinam intai pasii necesari pentru deturnarea unei sesiuni. In fiecare scenariu vom presupune ca session identifier-ul a fost compromis.
Cu un session mecanism simplistic nu este nevoie decat de un session identifier valid pentru a deturna o sesiune. Pentru a imbunatati aceasta situatie, sa vedem daca putem folosi ceva din request-ul HTTP pentru identificare.
Nota
Nu e bine sa te bazezi pe ceva la nivelul TCP/IP level, cum ar fi adresa IP, pentru ca aceste protocoluri nu au fost facute pentru activitati care au loc la nivele mai inalte (adica nivelul HTTP). Un singur utilizator poate avea o adresa IP diferita pentru fiecare request si utilizatori diferiti pot avea aceeasi adresa IP.
Sa ne amintim un request HTTP tipic:
GET / HTTP/1.1 Host: example.org User-Agent: Mozilla/5.0 Gecko Accept: text/xml, image/png, image/jpeg, image/gif, */* Cookie: PHPSESSID=1234
HTTP/1.1 cere in mod obligatoriu numai headerul Host, deci nu pare o idee buna sa ne bazam pe altceva. Totusi, ne intereseaza doar diferentele dintre headere, pentru ca dorim sa complicam impersonarea fara sa afectam uzabilitatea.
Sa ne imaginam ca requestul de mai inainte este urmat de un request cu un alt User-Agent:
GET / HTTP/1.1 Host: example.org User-Agent: Mozilla Compatible (MSIE) Accept: text/xml, image/png, image/jpeg, image/gif, */* Cookie: PHPSESSID=1234
Desi aceeasi cookie a fost prezentata, sa presupunem ca este acelasi utilizator? Pare putin probabil ca un browser sa schimbe headerul User-Agent intre request-uri. Sa modificam mecanismul sesiunii pentru a verifica acest lucru:
<?php
session_start();
if (isset($_SESSION['HTTP_USER_AGENT']))
{
if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT']))
{
/* Prompt for password */
exit;
}
}
else
{
$_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
}
?>
Acum un atacator trebuie sa prezinte atat un session identifier valid, cat si acelasi User-Agent header care a fost asociat cu sesiunea. Aceasta complica lucrurile un pic, si este deci un pic mai sigur.
Putem sa imbunatatim lucrurile? In general cookies sunt obtinute exploatand un browser vulnerabil ca Internet Explorer, dupa ce utilizatorul a vizitat site-ul atacatorului. Astfel, atacatorul va obtine header-ul User-Agent corect. Este nevoie de altceva pentru a proteja in acest caz.
Sa ne imaginam ca vom face in asa fel incat utilizatorul sa trimita MD5-ul pentru User-Agent in fiecare request. Nu mai este de ajuns ca atacatorul sa recreeze headerele pe care le contine requestul victimei, ci trebuie trimisa si aceasta infomatie extra. Desi nu e greu sa ghicesti constructia acestei dovezi, putem complica lucrurile alegand dovada intamplator:
<?php $string = $_SERVER['HTTP_USER_AGENT']; $string .= 'SHIFLETT'; /* Adauga orice date */ $amprenta = md5($string); ?>
Amintindu-ne ca session identifier este pastrat intr-un cookie, si aceasta inseamna ca un atac trebuie sa compromita cookieul (si probabil toate headerele HTTP), va trebui sa trimitem aceasta amprenta ca variabila URI. Aceasta trebuie inclusa in toate URI-urile, la fel ca session identifier-ul, deoarece amandoua trebuie verificate pentru ca o sesiune sa continue.
Pentru a ne asigura ca utilizatorii legitimi nu sunt tratati ca niste criminiali, daca informatia nu se verifica, vom cere parola din nou. Daca exista o eroare in mecanismul de verificare si un utilizator legitim este suspectat de impersonare, cererea parolei este cea mai delicata metoda de a rezolva situatia. De fapt unii utilizatori vor aprecia protectia oferita.
Exista multe metode folosite pentru a complica impersonarea si a proteja aplicatiile de session hijacking. Sper ca cel putin vei adauga ceva in plus fata de session_start() si poate vei veni si cu alte idei. Tine minte ca trebuie sa faci lucrurile grele pentru atacator, dar usoare pentru utilizatori.
Nota
Unii experti considera ca header-ul User-Agent nu este destul de consistent pentru a fi folosit cum am descris mai sus. Argumentul este ca un HTTP proxy intr-un cluster poate modifica headerul User-Agent in mod diferit de alti proxies in acelasi cluster. Desi eu nu am observat acest lucru (si folosesc cu incredere User-Agent), este un aspect pe care poate vrei sa-l iei in considerare.
Headerul Accept se poate schimba de la request la request in Internet Explorer (daca utilizatorul da refresh), deci acest header nu trebuie folosit.
< InapoiInainte >Shared HostsBaze de date si SQL