Ochrona przed SQL Injection - podstawy

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:

SELECT * FROM uzytkownicy WHERE user='' or 1=1 /*' AND haslo='blablabla'

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( "&"                        , "&"                 , $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';
}
Informacje
Ostatnia modyfikacja 09-01-2009 19:44 Ostatni autor borli
Ilość wyświetleń 5632 Wersja 8
Komentarz
Szczawik dnia 07-01-2009 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 dnia 09-03-2008 12:10
Jest takie coś jak htmlentitydecode/encode, które samo robi to co w 4 punkcie.
pzduniak dnia 08-03-2008 13:26
Zapomnieliście o htmlspecialchars :P
Twardy dnia 02-11-2007 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 dnia 29-10-2007 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 dnia 28-10-2007 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 dnia 28-10-2007 16:43
Ale nie podoba Ci się art? To są tylko podstawy :) są jeszcze ataki union select, ataki z % itp...
cyriel dnia 28-10-2007 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 dnia 28-10-2007 12:15
Nie zdarzają ci się literówki ?? ;>
Liczy się treść ;)

Copyright © 2000-2006 by Coyote Group 0.9.3-pre3
Czas generowania strony: 0.1238 sek. (zapytań SQL: 9)