Programowanie w języku PHP

Ochrona przed SQL Injection - podstawy

  • 2009-01-09 19:44
  • 12 komentarzy
  • 9848 odsłon
  • Oceń ten tekst jako pierwszy
1. Czym jest SQL Injection?
Ataki typu SQL Injection  polegają na "wstrzeleniu się" w zapytanie do bazy danych ze swoim kodem. Niestety wiele stron jest podatnych na ataki tego typu.

2. Na czym to polega?
Przykładem prostego SQL Injectiona jest nieprzefiltrowane zapytanie do bazy danych w skrypcie logowania.
$query = mysql_query("SELECT * FROM uzytkownicy WHERE user='".$_POST['user']."' AND haslo='".$_POST['haslo']."'");


Co tu jest nie tak? Wpiszmy zamiast użytkownika lub hasła znak '. Co się stało? Naszym oczom ukazał się błąd bazy danych. Ale jak to wykorzystać? MySQL ma bardzo wiele ciekawych funkcji, np. kiedy otworzymy komentarz, nie musimy go zamykać. Wpiszmy zamiast użytkownika coś takiego: ' or 1=1 /* a zamiast hasła byle co np.: blablabla. Zapytanie będzie wyglądało następująco:
<mysql>
SELECT * FROM uzytkownicy WHERE user='' or 1=1 /*' AND haslo='blablabla'
</mysql>
Kawałek zaznaczony na czerwono to podany przez nas użytkownik. Co dało nam /*? Zakomentowaliśmy całą resztę zapytania, która nie zostanie wykonana, czyli: ' AND haslo='blablabla'. Tak spreparowane zapytanie zawsze zwróci nam wartość TRUE (no chyba że 1≠1).

3. Jak się bronić?
Istnieją dwa rozwiązania. Oba są proste. Można użyć funkcji addslashes(), czyli kod powinien wyglądać tak:
$usr = addslashes($_POST['user']);
$pas = addslashes($_POST['haslo']);
$query = mysql_query("SELECT * FROM uzytkownicy WHERE user='".$usr."' AND haslo='".$pas."'");

Lub można używać htmlspecialchars():
$usr = htmlspecialchars($_POST['user']);
$pas = htmlspecialchars($_POST['haslo']);
$query = mysql_query("SELECT * FROM uzytkownicy WHERE user='".$usr."' AND haslo='".$pas."'");

Teraz nie musimy się obawiać ataku SQL Injection. Ale zostaje problem, gdy korzystam z htmlspecialchars() wtedy, jak to odczytuję, pokazują mi się dziwne znaki.

4. Jak to odczytać?
Jeżeli użyliście addslashes wtedy po prostu je odczytujecie z bazy danych. Ale, gdy używacie htmlspecialchars() - należy napisać własną funkcję która zamienia znaki. To jest przykład:
        function format_html($val)
    {
            if ( $val == "" )
            {
                    return "";
            }
 
            $val = str_replace( "&amp;"                        , "&"                 , $val );
            $val = str_replace( "&#60;&#33;--"        , "<!--"                  , $val );
            $val = str_replace( "--&#62;"                , "-->"               , $val );
            $val = str_replace( "&gt;"                        , ">"                  , $val );
            $val = str_replace( "&lt;"                        , "<"                  , $val );
            $val = str_replace( "&quot;"                , '"'                , $val );
            $val = str_replace( "&#036;"                , "$"                , $val );
            $val = str_replace( ""                                , "\r"          , $val );
            $val = str_replace( "&#33;"                        , "!"                 , $val );
            $val = str_replace( "&#39;"                        , "'"                 , $val );
 
            return $val;
    }

Po pobraniu funkcji musicie przepuścić ją przez funkcję format_html(). Jeżeli znak nie jest dodane do tej funkcji - możecie sami go dodać.

5. Jeszcze inaczej:
Warto sprawdzać dane jeszcze przed wciśnięciem ich w zapytanie i obsłużyć ewentualne błędy (np logować ip skąd biorą się takie błędy jeśli będzie się to powtarzać można zablokować stronę dla takiej osoby) dane których nie można weryfikować można przepuścić przez mysql_real_escape_string (uwaga przed użyciem tej funkcji musi być otwarte połączenie z bazą i trzeba pamiętać by mimo wszystko używać na zewnątrz cudzysłowu ograniczającego treść)
$usr = preg_match('/^[0-9]*$/', $_POST['user'])?$_POST['user']:FALSE;
if ($usr) {
    $query = mysql_query("SELECT * FROM uzytkownicy WHERE user='" . $usr . "' AND haslo='" . mysql_real_escape_string($_POST['haslo']) . "'");
} else {
    print 'Błędne dane wejściowe';
}

12 komentarzy

Brak avatara
Jorv 2014-04-07 11:14

Najlepiej jest używać funkcji addslashes(), doskonale zapobiega atakom na bazy MySQL.

Brak avatara
waqmaz 2014-01-27 17:34

Ja używam wyłącznie prepared statements.

Szczawik 2009-01-07 01:33

No i brakuje opisania podstawowego mechanizmu, zapobiegajacego SQL Injection - zapytan parametrycznych. Majac dane przesylane w osobnych parametrach nie ma mozliwosci wykonania tego typu ataku, bez wzgledu na tresc i forme danych.

nav 2008-03-09 12:10

Jest takie coś jak htmlentitydecode/encode, które samo robi to co w 4 punkcie.

pzduniak 2008-03-08 13:26

Zapomnieliście o htmlspecialchars :P

Twardy 2007-11-02 15:28

A czy poprzez php nie mozna laczyc sie z bazami typu MSSql? Tam tego typu praktyki nic nie daja i wcale nie chronia. Inna sprawa, ze jezeli chodzi o mysql mamy jeszcze Mysql_real_escape_string().

MikiKam 2007-10-29 21:40

Jak zdążyliście (chyba) zauważyć w tytule artykułu to są zupełne podstawy. Jeżeli nie podoba się komuś albo ma inną koncepcję artykułu to proszę go zmienić :)

Adam Boduch 2007-10-28 19:05

Artykul prezentuje bardzo niski poziom merytoryczny. Wiecej na ten temat w wikipedii jest napisane juz. Przydaloby sie wiecej przykladow i wiecej tresci zeby zrobic naprawde, tekst na poziomie. Np.: chociazby przetlumaczyc z:

http://www.unixwiz.net/techtips/sql-injection.html
http://en.wikipedia.org/wiki/SQL_injection
http://www.phpfreaks.com/phpma[...]ty.database.sql-injection.html

MikiKam 2007-10-28 16:43

Ale nie podoba Ci się art? To są tylko podstawy :) są jeszcze ataki union select, ataki z % itp...

cyriel 2007-10-28 14:13

W ramach ciekawostki mogłeś jeszce dodać, że kiedyś słynna 'szkoła hakierów' została zaatakowana tym atakiem i komuśtam udało się odczytać informacje o użytkownikach i chyba cośtam jeszcze :)

MikiKam 2007-10-28 12:15

Nie zdarzają ci się literówki ?? ;>
Liczy się treść ;)