PHP

Ochrona przed SQL Injection - podstawy

MikiKam

     1 Czym jest SQL Injection?
     2 Jak się bronić?
               2.1 Inne silniki baz danych
     3 Anty-wzorce

Czym jest SQL Injection?

Atak typu SQL Injection wykorzystują lukę w tworzeniu zapytań do baz danych. Polegają na swobodnym umieszczaniu arbitrarnych danych bezpośrednio w zapytaniu SQL, np. danej otrzymanych od użytkownika (imię, nazwisko). Jeśli imię zawierałoby znaki specjalne SQL (np. apostrof, cudzysłów, backslash, myślnik) wtedy dana od użytkownika zostanie potraktowana jako część zapytania, a nie jako dana wejściowa.

Przykładem prostego SQL Injectiona jest nieprzefiltrowane zapytanie do bazy danych w skrypcie logowania.

Oto zupełnie bezpieczne zapytanie SQL:

$query = mysql_query("SELECT * FROM uzytkownicy WHERE user='Kamil' AND haslo='password'");

Jego autor, chciałby żeby nazwa użytkownika oraz hasło mogły być dynamicznie pobierane od użytkownika, więc wprowadza taką zmianę:

$query = mysql_query("SELECT * FROM uzytkownicy WHERE user='".$_POST['user']."' AND haslo='".$_POST['haslo']."'");

Autor wyobraża sobie że w zmiennej $_POST['user'] oraz $_POST['haslo'] będą zawierać dane typu: "Paweł", "Jacek", etc.

Co jest nie tak w takim podejściu? Od razu widać że dane wprowadzone przez użytkownika ($_POST['user'] oraz $_POST['haslo']) są bezpośrednio dołączone do zapytania, a to znaczy że mogą posłużyć zarówno jako dana (np "login" lub "hasło"), ale również jako część samego zapytania. Wystarczy że użyjemy znaku zamknięcia stringa '. Uruchommy program z nazwą użytkownika o treści: admin' --. Zapytanie będzie wyglądało następująco:

SELECT * FROM uzytkownicy WHERE user='admin' --' AND haslo='blablabla'

Kawałek admin' -- to podany przez nas użytkownik. Co dało nam '? Dzięki temu zamknęliśmy część STRING zapytania SQL. Co dało nam więc --? Zakomentowaliśmy całą resztę zapytania, która nie zostanie wykonana (głównie wyłączymy ' AND haslo='blablabla', czyli sprawdzanie hasła) i nie będziemy musieli się martwić błędem składni spowodowanym przez niedomknięty '. Tak spreparowane zapytanie zawsze zwróci nam record administratora, dlatego że user='admin', a warunek z hasłem jest zignorowany.

Jak się bronić?

Warto sprawdzać dane jeszcze przed wciśnięciem ich w zapytanie (np danych typu int oraz bool nie trzeba obsługiwać, ponieważ nie niosą zagrożenia) 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() (jeśli korzystamy z MySql). Pamiętajcie tylko, że przed użyciem tej funkcji musi być otwarte połączenie z bazą i trzeba pamiętać by mimo wszystko używać tej funkcji na zewnątrz cudzysłowu ograniczającego treść.

mysql_query("SELECT * FROM uzytkownicy WHERE user='" . mysql_real_escape_string($user) . "' AND haslo='" . mysql_real_escape_string($_POST['haslo']) . "'");

Inne silniki baz danych

Jeśli korzystasz z innych silników baz danych skorzystaj z:

Anty-wzorce

Nie należy do obrony przed SQL Injection stosować funkcji:

Funkcje wyżej nie mają nic wspólnego z bazami danych, i, co prawda zabezpieczą przed prostym atakiem (z zamknięciem stringa i komentarzem '--) ale pozwolą na bardziej zaawansowany sposób wykorzystania zapytań SQL. Jedyną wiarygodną funkcją do budowania zapytań ręcznie jest mysql_real_escape_string().

Jeśli już mowa o anty-wzorcach, to już samo sklejanie zapytań ręcznie jest anty-wzorcem. Poprawnie, należałoby skorzystać z PDO (warstwy abstrakcji, tzw "prepared statements") lub z ORM (np Eloquent z frameworka Laravel lub ORM z lżejszego frameworka Ouzo).

PHP

10 komentarzy

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 :)

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

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

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ć :)

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().

Zapomnieliście o htmlspecialchars :P

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

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.

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/phpm[...]y.database.sql-injection.html