Ivan Marković

Security consultant and researcher

Long experience in designing and implementation of security solutions, mainly oriented on web, mobile and embedded applications. Author of penetration testing tools, recognized by OWASP organization and BackTrack Linux distribution. Researching work includes discovery of vulnerabilities of numeral applications and services, and for these, author received public apreciations by Microsoft Company..

Contact via Linkedin or read interesting staff on Twitter.

"Race Condition" and web technology

"Race Condition" i web tehnologije

Ivan Marković (17.10.2006.)
Koliko god se razvijale nove tehnologije uvek ćemo se susretati sa nekim sigurnosnim problemima kod implementacije samih rešenja neke tehnologije ili kod neadekvatnog korišćenja iste. Postoji mnogo rešenja koja služe da odklone dobro poznate propuste u samim okruženjima kao sto su razni prelivi bafera i ubacivanja neželjenog koda kao i rešenja koja sluze da upozore programera na greške ili da mu uskrate mogućnost da istu napravi. Ali, opet postoji mogućnost da se greška napravi kao sto ćemo i videti u nastavku teksta.

Race condition je poznat pojam svim programerima i on se pojavljuje kada dva ili vise procesa/niti pristupa jednom zajedničkom/globalnom resursu i vrši neke operacije nad njim. T.j. mi predvidjamo i očekujemo da nam se redosled operacija nad nekim resursom izvrši prethodno definisanim/predvidjenim redosledom ali usled desihronizacije i/ili nepostojanja zaštitnog sistema redosled operacija se može pomeriti tako da na kraju ne dobijemo željeni rezultat.

Svrha ovog texta nije da se detaljno bavimo proučavanjem ovog problema pa shodno time preporučujem onima koji su manje upućeni u ovaj problem da pročitaju sledeći tekst.

Jednostavan primer pojave ovog problema je kod korišćenja sesija u php-u. Ako bi vise skriptova koristilo jednu istu varijablu iz sesije istovremeno, uskoro bi došlo do velike greške u rezultatima naših skriptiaplikacija. Zato, u php-u postoji sistem koji zaključava podatak iz sesije prilikom pristupa pojedinačnog scripta i tek posle završetka tog skripta podatak se odključava i daje na obradu sledećem. Vise o ovom sistemu pročitajte ovde. Sličan problem se javlja i prilikom korišćenja nekog polja u bazi. Problem se takodje rešava sistemom "zaključavanja" i vise o njemu (kod mysql-a) možete pročitati na sledećem linku.

Jos jedan primer sa kojim se srećemo svakog dana je kod editovanja strana u raznim CMS-ovima i sličnim aplikacijama. Sa obzirom da u ovakvim aplikacijama postoji mogućnost da se vise korisnika uloguje istovremeno i želi da edituje jednu istu stranicu može doći do pojave Race Condition-a. Jednostavno rešenje za ovaj problem ima aplikacija DocuWiki: Prilikom zahteva za editovanje neke stranice postavlja se flag koji označava da je stranica zauzeta tj zaključana. Posle 15 minuta (po default podešavanjima) neaktivnosti ili submitovanja izmena, skida se flag zaključavanja ili se prebacuje na sledećeg korisnika.

Dummy primer:

Sa obzirom da se u većini slučajeva ova greška javlja zbog previda programera a ne zbog same platforme i alata koji su korišćeni želim da vam demonstriram pojavu ovog propusta kod tehnologije koja se danas veoma često koristi a to je AJAX. Neću vam govoriti sta je AJAX i gde ga treba koristiti ili ne, već ću vam samo dati jedan banalan primer njegove loše upotrebe tj pojave Race Condition propusta.

Zamislimo da imamo sistem u kome je korisnik ulogovan tj ima privilegije da pozove odredjene funkcije. Te privilegije se čuvaju u sesiji. Dalje, korisnik želi da pozove funkciju koja će da mu generiše šifru i da ga zatim izloguje, da bi se ulogovao sa novom. Krenimo od trenutka poziva ove funkcije tj user.php fajla koji je zadužen za generisanje nove šifre, logout-a, ... :

<?php
// Započinjemo sesiju sa odredjenim privilegijama
session_start();
$_SESSION['test'] = 1;
?>

<html>
<head>
<title>Test page ...</title>
</head>

<script>
var zahtev1; // Ovaj zahtev treba da generiše 
             //novu šifru,
var zahtev2; // a ovaj da nas izloguje ...

function onLoad() {
  zahtev1 = new XMLHttpRequest();
  zahtev2 = new XMLHttpRequest();
  zahtev1.open('GET', 'func.php?com1', true);
  zahtev2.open('GET', 'func.php?com2', true);
  zahtev1.onreadystatechange = handleResponse1;
  zahtev2.onreadystatechange = handleResponse2;
  zahtev1.send('');
  zahtev2.send('');
}

function handleResponse1() {
  document.getElementById('test1').innerHTML = 
    zahtev1.responseText;
}
function handleResponse2() {
  document.getElementById('test2').innerHTML = 
    zahtev2.responseText;
}

</script>

<body onLoad='onLoad();'>
  <div id='test1'></div>
  <br>
  <div id='test2'></div>
</body>
</html>
Kao što vidimo imamo dva zahteva za koje očekujemo da se izvrše sledećim redosledom: zahtev1, zahtev2.
Dalje, da vidimo šta se dešava u func.php :
<?php
session_start();
// ...

if (isset($_GET['com1']))
{
  // Trošimo procersko vreme
  for ($x = 0; $x < 15000; $x++) strlen('junk');
  if ($_SESSION['test'] == 1) {
    $retString ='Vasa sifra je:' . rand() . time();
  }
  else $retString = 'Niste ulogovani !';
}
else if (isset($_GET['com2'])) {
  // Trošimo procersko vreme
  for ($x = 0; $x < 15000; $x++) strlen('junk');
  $_SESSION['test'] = '';
  $retString = 'Odlogovani ste !';
}
echo $retString;
?>
Ovde imamo if uslov koji u zavisnosti od zadatog argumenta (com1,com2) izvršava odredjene akcije. T.j. kako očekujemo izvršiće se prvo zahtev1 i deo koda u kome se generiše šifra jer jos uvek u sesiji imamo varijablu 'test' u kojoj su zapisane naše privilegije (u našem slučaju to je vrednost 1). Zatim treba da se izvrši zahtev2 tj deo koda u kome se brišu naše privilegije iz 'test' varijable i deo u kome se odjavljujemo sa sistema. Takodje u prvom delu koda, deo u kome se generiše šifra, imamo i proveru privilegija i ako kojim slučajem nemamo odredjene privilegije ('test' != 1) dobićemo poruku da nismo ulogovani.

* Imamo i deo koda koji služi da troši procesorsko vreme da bi se ova greška česće pojavljivala.

Ako probate da pokrenete ovu test aplikaciju na vašem serveru videćete da nećete dobiti uvek iste rezultate. T.j. neće se uvek prvo izvrsiti zahtev1 vec će se u nekim slučajevima prvo izvrsiti zahtev2, i samim tim ne dobijamo željeni rezultat.

Ova pojava se ispoljava zbog same prirode rada računara tj procesora i procesa. Za svaki zahtev koji smo inicijalizovali kreiran je odvojen proces koji dobija svoje vreme kod procesora. Usled nekih drugih radnji dodela vremena ne mora uvek teći redosledom koji mi očekujemo pa samim tim može doći do ukrštanja raznih stadijuma izvršenja odredjenih funkcija nekih procesa, a koji su usko vezani za neki zajednički resurs, i pojave grešaka koje smo demonstrirali.

Savet kao rešenje:

Sa ovim propustom se susrećemo svakog dana, vecina CMS-ova koji se vrte na vebu nemaju sistem za hendlovanje višetrukog pristupa korisnika prilikom editovanja zajedničkih stranica, sa pojavom AJAX-a se sve više pojavljuju propusti kao u primeru koji sam vam demonstrirao. Ako već koristite ovakve tehnologije ili ako razvijate sistem koji opslužuje vise korisnika istovremeno onda morate razviti i sistem koji ce da upravlja ovakvim akcijama. Zapamtite da se vaš scriptprogram ne izvršava neprekidno već iz delova i da se može prekinuti izmedju bilo koje dve instrukcije.

Zato prilikom pisanja vitalnih funkcija pripazite na delove koji se moraju izvršiti bas tim redosledom tj bez interakcije sa nekim drugim funkcijama. Takodje, zapamtite da se istovremeno može pokrenuti vise instanci vašeg programaskripta i da u tim slučajevima obavezno implementirate sistem zaključavanja zajedničkih resursa.

 

http://web.archive.org/web/20080705113314/http://novetehnologije.com/ShowText/48/Race-Condition-i-web-tehnologije.aspx