Zapobieganie atakom SQL INJECTION w przykładach PHP / kodu

po kilku latach testów penetracyjnych i pracy z zespołami programistycznymi w celu zabezpieczenia ich systemów, zauważyłem, że nie ma kompletnego postu na blogu, który zawiera szczegółowe wyjaśnienie, jak zapobiegać atakom SQL injection we wszystkich najpopularniejszych językach i frameworkach aplikacji internetowych. Dlatego postanowiłem napisać serię wielu postów, aby uczynić go najbardziej kompletnym przewodnikiem zapobiegającym SQL injection.

ten poradnik jest otwartą serią postów i jest to pierwszy post. Jeśli chcesz, abym dodał dowolną technologię, proszę o komentarz poniżej, a będę bardzo szczęśliwy, aby wyjaśnić zapobieganie SQL injection dla tej technologii. Ten przewodnik przeanalizuje następujące technologie:

  • PHP
    • Laravel
    • Symfony
    • CodeIgniter
    • CakePHP
    • FuelPHP
    • Zend
  • JEE
    • JDBC
    • JPA
  • ASP.NET
    • ADO.NET
    • ADO.Net Dopper
    • NHibernate

ten pierwszy post skupi się na języku PHP i wszystkich jego frameworkach. Kolejne artykuły będą dotyczyć innych wymienionych technologii.

dla tych, którzy nie wiedzą, co to jest atak SQL injection, pozwólcie, że zrobię małe wprowadzenie, aby im to wyjaśnić. Ataki SQL injection zdarzają się, gdy zły użytkownik próbuje wstrzyknąć złośliwe żądanie SQL do uzasadnionego żądania. Wpływ ataku SQL injection różni się w zależności od sytuacji w zależności od wielu elementów związanych ze środowiskiem aplikacji i może przejść od “prostego” jak wyciek informacji do pełnej kontroli serwera. Nie będę zagłębiał się w sposób, w jaki atakujący mógłby wykorzystać tę lukę ani w jaki sposób możemy ją odkryć w testach blackbox, ponieważ nie jest to cel tego postu. Zobaczymy, jak to odkryć w kodzie źródłowym i jak to naprawić.

jak zapobiec atakowi SQL injection w czystym kodzie źródłowym PHP?

zacznijmy naszą podróż od jednej z najstarszych i popularnych technologii programistycznych PHP bez żadnych frameworków. Teraz pierwszą rzeczą do zrobienia jest wiedzieć, jak wygląda podatna linia kodu źródłowego, aby móc zidentyfikować podatność.

oto przykład żądania uwierzytelnienia:

$request = "SELECT * FROM users WHERE username=". $_POST ." AND password = ". $_POST;
$result = $mysqli->query($request);

teraz, jeśli przyjrzysz się bliżej żądaniu, zauważysz, że parametry użytkownika $_POST i $_POST są bezpośrednio wstrzykiwane do żądania SQL bez filtrowania. Czyni to tę linię kodu podatną na SQL injection.

aby naprawić tę lukę, musisz przefiltrować dane przed użyciem ich w żądaniu sql. Aby to zrobić, najlepszym rozwiązaniem jest użycie przygotowanych oświadczeń. Oto przykład, w jaki sposób można użyć przygotowanych instrukcji, aby naprawić tę lukę:

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param($username, $password);
$stmt->execute();

dobra praktyka

masz możliwość nie używać funkcji bind_param () oto przykład jak to zrobić :

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=:username AND password=:password");
$stmt-> execute(array('username'=>$_POST,'password'=>$_POST));

po latach analizowania kodów źródłowych … odkryłem interesujący błąd, który wielu programistów robi podczas naprawiania tej luki. Spójrz na poniższy przykład:

$stmt = $conn->prepare("SELECT * FROM users WHERE username=".$_POST." AND password=".$_POST);

jeśli spojrzysz na ten przykład, zobaczysz, że korzystamy z przygotowanych oświadczeń. Jednak parametry są bezpośrednio wstrzykiwane w żądaniu SQL. Jest to bardzo częsty błąd popełniany przez programistów i sprawia, że kod jest podatny na SQL injection, nawet jeśli używamy przygotowanych instrukcji.

ideą przygotowanych instrukcji jest filtrowanie parametrów przed wstrzyknięciem ich do żądania SQL … miej to na uwadze.

bardzo ważna uwaga:

PHP oferuje kilka innych funkcji do filtrowania żądań SQL i zapobiegania wstrzykiwaniu SQL, ale niektóre z nich nie są wydajne. Na przykład funkcja mysql_real_escape_string() jest jedną z najbardziej znanych funkcji PHP zapobiegających wstrzykiwaniu SQL. Niestety ta funkcja jest naprawdę wydajna. Korzystając z tej funkcji, Biblioteka MySQL doda ukośnik wsteczny do następujących znaków: NULL, \ x00, \ n, \ r,\,’, ” i \ x1a. jednak w sytuacji takiej jak ta:

$id = mysql_real_escape_string("1 OR 1=1");
$request = "SELECT * FROM users WHERE id = $id";

w kolorze czerwonym jest to, co zły Użytkownik wyśle Ci

mysql_real_escape_string () nie ochroni Cię przed takim atakiem.

dlatego zawsze polecam użycie przygotowanych instrukcji zamiast tej funkcji.

jak zapobiec atakowi SQL injection w frameworku Laravel ?

teraz, gdy widzieliśmy atak SQL injection przeciwko czystemu kodowi PHP, nadszedł czas, aby zobaczyć, jak możemy naprawić lukę w aplikacji internetowej opartej na frameworku. Zanim zaczniemy o tym mówić, pozwólcie, że najpierw przedstawię wam mały opis struktury Laravela.

Laravel jest open-source web framework napisany w PHP z poszanowaniem zasady model-view-controller i w całości opracowany w programowaniu obiektowym.

przykład 1

oto przykład podatnego żądania SQL w laravel :

$user = DB::select('select * from users where username='. $request->post('username').' AND password='. $request->post('password'));

framework Laravel oferuje niesamowite funkcje, które pomagają programistom zabezpieczyć żądanie SQL przed złośliwymi zastrzykami. Niestety większość luk w zabezpieczeniach SQL injection, które wykrywam w takich aplikacjach, jest związana z niewłaściwym wykorzystaniem funkcji Laravel.

przeanalizujmy poprzedni przykład, zgodnie z dokumentacją Laravela funkcja DB:: select może otrzymać dwa parametry. Pierwszy to żądanie SQL, a drugi to parametry. Tak więc aby filtrować parametry dla SQL injection, musisz wstawić parametry w żądaniu za pomocą drugiego parametru, tak jak ten przykład:

$user = DB::select (‘select * from users where username=? A hasło=?’, );

przykład 2

Laravel ma wiele sposobów komunikacji z bazą danych w tym przykładzie zobaczysz, że laravel zmusza programistę do użycia niektórych funkcji, które automatycznie filtrują dane Użytkownika, jak w poniższym przykładzie:

Users::where('username', $request->get('username'))->orderBy($request->get('orderby'))->get();

ta funkcja ma być zabezpieczona przed SQL injection i są, jedyny problem jest na poziomie funkcji orderBy (). Zgodnie z dokumentacją Laravela, ta funkcja nie filtruje danych użytkownika, więc atak SQL injection jest nadal możliwy dzięki tej funkcji.

najlepszą rzeczą do zrobienia jest nie dawanie Użytkownikowi możliwości kontrolowania nazwy tabeli, ale jeśli jest to coś nieuniknionego, będziesz musiał użyć białej listy, aby zweryfikować dane użytkownika przed wstawieniem ich do tej funkcji.

jak zapobiec atakowi SQL injection w Symfony ?

zobaczmy inny znany framework PHP, który oferuje zestaw komponentów PHP wielokrotnego użytku, aby przyspieszyć tworzenie aplikacji PHP. Symfony jest również używany przez niektóre z najbardziej znanych CMS internetowych, takich jak Drupal i Magento.

Symfony jest jednym z najbezpieczniejszych frameworków, z którymi możesz pracować, oferuje szeroką liczbę funkcji do pisania bezpiecznego kodu. Jeśli jednak te funkcje nie są dobrze używane, otrzymasz kod podatny na ataki. Oto przykład podatnego kodu:

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > '. $request->query->get('price')
);

przeanalizujmy teraz ten podatny na ataki kod. Zgodnie z dokumentacją Symfony, createQuery () jest funkcją, która domyślnie używa przygotowanych instrukcji. Tak więc obiekt wynikający z takiej funkcji daje dostęp do funkcji setParameter (). Ten filtruje wszystkie dane użytkownika, aby uniknąć wstrzyknięcia SQL. Oto przykład jak z niego korzystać :

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', $price);

jak zapobiec atakowi SQL injection w Codeigniter ?

CodeIgniter jeden z najbardziej wydajnych, lekkich i popularnych frameworków PHP o bardzo małym rozmiarze. Został stworzony dla programistów, którzy potrzebują prostego i eleganckiego zestawu narzędzi do tworzenia w pełni funkcjonalnych aplikacji internetowych. Podobnie jak Symfony i Laravel, Codeigniter jest również wyposażony w niektóre systemy zabezpieczeń, aby pomóc programistom w tworzeniu bezpieczniejszych aplikacji.

jednak również z Codeigniter niektórzy programiści popełniają ten sam błąd i wstrzykują dane Użytkownika bez filtrowania. Oto przykład takich błędów, które prowadzą do ataku SQL injection:

$query = 'SELECT * FROM users WHERE username = '. $this-> input->post('username'). ' AND password= '. $this-> input->post('password');
$this->db->query($query);

teraz, aby rozwiązać ten problem, trzeba będzie spojrzeć na dokumentację Codeigniter. Zgodnie z nim funkcja query() przyjmuje dwa parametry. Pierwszy to zapytanie sql, a drugi to parametry, które chcesz powiązać.

przykład 1

domyślnie ta funkcja filtruje wszystkie powiązane parametry, aby zapobiec zastrzykom SQL. Oto przykład prawidłowego użycia tej funkcji:

$query = 'SELECT * FROM users WHERE username = ? AND password = ? ';
$this->db->query($query, array($this->input->post('username'), $this-> input-> post('password')));

przykład 2:

Codeigniter zapewnia inny sposób wykonywania żądań SQL, zapobiegając zastrzykom SQL. Oto przykład użycia klasy Active Record do zapobiegania zastrzykom SQL:

$this->db->get_where('users',array('username'=>$this->input->post('username') ,'password' => $this->input->post('password')));

funkcja get_where () nie istnieje już w wersji 4 Programu Codeigniter.

notka 1

Codeigniter oferuje również funkcję, która wygląda jak, mysql_real_escape_string () funkcja o nazwie escape(). Ale, jak powiedziałem po raz pierwszy mysql_real_escape_string() może być pominięte w niektórych przypadkach i trzeba go unikać. Fakt, że funkcja escape() robi dokładnie to samo, wtedy możliwe byłoby również jej ominięcie. Dlatego nie zachęcam programistów do używania funkcji escape ().

notka 2

jak zapobiec atakowi SQL injection w CakePHP ?

CakePHP to szybki framework programistyczny dla PHP, który wykorzystuje powszechnie znane wzorce projektowe, takie jak asocjacyjne mapowanie danych, Front Controller i MVC. CakePHP oferuje kilka wbudowanych komponentów, które ułatwiają tworzenie aplikacji internetowych i zwiększają ich bezpieczeństwo.

jednak aplikacje CakePHP są również podatne na zastrzyki SQL, jeśli nie są dobrze używane. Oto przykład kodu podatnego na atak SQL injection w tym frameworku:

$results = $connection-> execute('SELECT * FROM users WHERE username = '. $this->request->getParam('username').' AND password='. $this->request->getParam('password'))->fetchAll('assoc');

funkcja execute () domyślnie używa przygotowanych instrukcji. Jednak poprzedni przykład nadal jest podatny na atak SQL injection, ponieważ dane użytkownika są wstawiane bezpośrednio do uzasadnionego żądania sql. Oto przykład właściwego sposobu użycia tej funkcji:

$results = $connection->execute('SELECT * FROM users WHERE username = :username AND password=:password', )->fetchAll('assoc');

framework CakePHP oferuje również system o nazwie Query Builder, który wymusza filtrowanie danych użytkownika, aby zapobiec atakom SQL injection. Zgodnie z dokumentacją CakePHP, pod osłonami Konstruktor zapytań używa przygotowanych instrukcji.

oto przykład jak można używać takiego systemu we właściwy sposób:

use Cake\ORM\Locator\LocatorAwareTrait;
$users = $this->getTableLocator()->get('users');
// Start a new query.
$query = $users->find();
$query->where();

jak zapobiec atakowi SQL injection w FuelPHP ?

FuelPHP jest jednym z najnowszych frameworków PHP, który powstał w oparciu o najlepsze pomysły każdego frameworka na rynku. Został opracowany w PHP 5 i jest w pełni obiektowy. W FuelPHP bezpieczeństwo było frontem i centrum zainteresowania, które zmusiło współpracowników do wdrożenia wielu mechanizmów bezpieczeństwa w celu filtrowania danych użytkowników. Ataki SQL injection jednym z powodów wdrożenia takich mechanizmów w FuelPHP.

jednak nawet przy tak potężnym frameworku, proste niewłaściwe użycie tych mechanizmów naraża cały kod na atak SQL injection. Oto przykład takiego błędu kodu:

$query = "SELECT * FROM article WHERE id = ". Input::get('id');
$result = DB::query($query)->execute();

aby to naprawić, istnieją również dwie techniki, takie jak inne ramy. Pierwszym z nich jest prawidłowe użycie funkcji query (), poprzez powiązanie parametrów, takich jak Poniższy przykład :

$query = "SELECT * FROM article WHERE id = :id"; // our query
$result = DB::query($query)->bind('id', Input::get('id'))->execute();

drugim rozwiązaniem jest wykorzystanie mechanizmu ORM zaimplementowanego w frameworku FuelPHP do komunikacji z bazą danych. Oto przykład jak z niego korzystać:

$user = DB::select()->from('article')->where('id', Input::get('id'))->execute();

jak zapobiec atakowi SQL injection w zend framework ?

Zend Framework (zmieniony na Laminas Project) jest jednym z najbardziej znanych frameworków w świecie PHP. Został opracowany z myślą o dostrojeniu wydajności, co wyjaśnia, dlaczego każda nowa wersja jest znacznie szybsza niż stara. Zend został również opracowany przy użyciu najlepszych praktyk bezpieczeństwa, co zmusiło jego programistów do wdrożenia dodatkowych mechanizmów bezpieczeństwa do radzenia sobie ze znanymi zagrożeniami cybernetycznymi.

niektóre z tych mechanizmów zostały zaimplementowane do radzenia sobie z atakami SQL injection. Ale jak zawsze nadużycie tych mechanizmów prowadzi do podatnego kodu. W tej części artykułu pokażę przykład takiego błędu:

$adapter->query('SELECT * FROM `article` WHERE `id` = '. $this->getRequest()->getPost('id'));

oto jak naprawić tę lukę:

$adapter->query('SELECT * FROM `article` WHERE `id` = ?', );

Leave a Reply