kompletny przewodnik po sql injection dla developerów php (i nie

64
Copyright © The OWASP Foundation Permission is granted to copy, distribute and/or modify this document under the terms of the OWASP License. The OWASP Foundation OWASP http://www.owasp.org Kompletny przewodnik po SQL injection dla developerów PHP (i nie tylko) Krzysztof Kotowicz PHP Developer http://web.eskot.pl Medycyna Praktyczna [email protected] 10.03.2010

Upload: tranthien

Post on 11-Jan-2017

232 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

Copyright © The OWASP FoundationPermission is granted to copy, distribute and/or modify this document under the terms of the OWASP License.

The OWASP Foundation

OWASP

http://www.owasp.org

Kompletny przewodnik po SQL injection dla developerów PHP (i nie tylko)

Krzysztof KotowiczPHP Developer

http://web.eskot.plMedycyna [email protected]

Page 2: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 2

Plan prezentacji

Co to jest SQL injection? Dlaczego SQL injection jest groźne (demo)? Jak się bronić?

• Prepared statements• Escape'owanie• Procedury składowane• Metody uzupełniające

Podsumowanie

Page 3: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 3

Omawiane bazy danych (RDBMS)

MySQL Oracle MS SQL Server W mniejszym stopniu:

• PostgreSQL• SQLite

Page 4: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 4

Omawiane projekty PHP PDO – PHP data objects

• Wspólny interfejs dla różnych RDBMS Doctrine 1.2

• ORM (Object Relational Mapper) używany m.in. we frameworku Symfony

Propel 1.4• ORM konkurencyjny dla Doctrine• Używany we frameworku Symfony

Zend Framework 1.10• Popularny framework MVC dla PHP

MDB2 2.4.1• Warstwa abstrakcji bazy danych (DBAL)• Dystrybuowany przez PEAR

Page 5: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 5

Co to jest SQL injection?

Page 6: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 6

SQL injection – krótka definicjaJest to rodzaj ataku na aplikacje internetowe.Polega na tym, że dane od użytkownika pochodzące z:

URL: www.example.com?id=1Formularzy: [email protected] elementów: np. cookie, nagłówki HTTP

zostają zmanipulowane tak, że w podatnej aplikacji zostaje wykonane „wstrzyknięte” przez atakującego polecenie SQL.

Page 7: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 7

Przykład – formularz logowania

Użytkownik jest zalogowany bez znajomości loginu ani hasła

SELECT * FROM users WHERE login = '{$login}' and password_hash = MD5('{$password}')

$login = "' or 1=1 -- ";$password = "dowolne";

// zamierzalismy osiagnac to (kod \ dane)SELECT * FROM users WHERE login = '' or 1=1 -- ' and password_hash = MD5('dowolne')

// serwer interpretuje to takSELECT * FROM users WHERE login = '' or 1=1 -- ' and password_hash = MD5('dowolne')

SELECT * FROM users WHERE login = '{$login}' and password_hash = MD5('{$password}')

$login = "' or 1=1 -- ";$password = "dowolne";

// zamierzalismy osiagnac to (kod \ dane)SELECT * FROM users WHERE login = '' or 1=1 -- ' and password_hash = MD5('dowolne')

SELECT * FROM users WHERE login = '{$login}' and password_hash = MD5('{$password}')

$login = "' or 1=1 -- ";$password = "dowolne";

SELECT * FROM users WHERE login = '{$login}' and password_hash = MD5('{$password}')

Page 8: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 8

Dlaczego jest groźne? DEMO

Page 9: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 9

Czym grozi podatność na SQL injection?

Nieuprawniony dostęp do aplikacji Dostęp do całej zawartości bazy / baz

na serwerze Denial of service Możliwość modyfikacji danych w bazie Przeczytanie / zapisanie pliku na

serwerze Wykonanie kodu na serwerze

Page 10: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 10

Kilka faktów

Podatności na injection na pierwszym miejscu OWASP Top 10 2010 RC

Odpowiada za 40–60% przypadków wycieku danych [1] [2]

Obecne techniki ataku są bardzo zaawansowane i często automatyzowane

• Podatność nie tylko w części WHERE • Czasem celem jest zepsucie zapytania

Codziennie znajdowane podatności, nawet w nowych aplikacjach

Page 11: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 11

Jak się bronić?

Page 12: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 12

Jak się bronić przed SQL injection?

Źródło podatności - łączenie kodu z danymi

Metody obronyOddzielenie kodu od danych

prepared statementsstored procedures

Escape'owanie danych

SELECT * FROM users WHERE login = 'login'

Page 13: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 13

Jak się bronić?Prepared statements

Page 14: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 14

Prepared statements – zasada działania

1. Przygotowujemy polecenie SQL (string) W miejsce danych wstawiamy znaczniki

2. Przesyłamy polecenie na serwer3. Podajemy zestaw danych do polecenia4. Wykonujemy polecenie5. Odbieramy rezultat

3, 4, 5 można powtarzać...6. Czyścimy polecenie

WHERE a = ? ... WHERE a = :col

PREPARE

EXECUTE

Page 15: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 15

Prepared statements - przykład

Przykład działania (PDO)// przygotowujemy zapytanie$stmt = $dbh->prepare("INSERT INTO SUMMARIES (name, sum) VALUES (:name, :sum)");

// podajemy wartosci zmiennych – RAZEM Z TYPAMI!$stmt->bindParam(':name', $name, PDO::PARAM_STR);$stmt->bindParam(':sum', $sum, PDO::PARAM_INT); // podajemy wartości zmiennych$name = 'something';$value = 1234;

// wykonujemy zapytanie$stmt->execute(); $stmt = null; //zwalniamy pamiec

Page 16: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 16

Prepared statements - zalety

Polecenia SQL są całkowicie oddzielone od przetwarzanych danych

Brak możliwości wstrzyknięcia kodu SQL Polecenie SQL jest przez serwer kompilowane

tylko raz – potencjalne zwiększenie wydajności zapytań

$stmt->bindParam(':name', $name, PDO::PARAM_STR);$stmt->bindParam(':sum', $sum, PDO::PARAM_INT);

// petla po danych...foreach ($do_bazy as $name => $value) { $stmt->execute();}

Page 17: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 17

Prepared statements - uwagi

Nie wszystkie typy poleceń można parametryzować

Nie w każdym miejscu polecenia można wstawić parametr

Samo ich użycie nie wymusza stosowania parametrów

Czasem są emulowane (ale to dobrze!)

-- bladSELECT * FROM :tabelaSELECT :funkcja(:kolumna) FROM :widok

-- nie tego się spodziewacieSELECT * FROM tabela WHERE :kolumna = 1SELECT * FROM tabela GROUP BY :kolumna

Page 18: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 18

Prepared statements w Doctrine

Używa PDO (emulacja dla Oracle) i prepared statements

Zamiast SQL używa własnego języka – DQL$q = Doctrine_Query::create() ->select('u.id') ->from('User u') ->where('u.login = ?', ‘mylogin');

echo $q->getSqlQuery();// SELECT u.id AS u__id FROM user u // WHERE (u.login = ?)

$users = $q->execute();

Page 19: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 19

Prepared statements w Doctrine cd.

Wciąż można „wpaść”

Trzeba poprawić na:

NIGDY nie umieszczaj danych wejściowych bezpośrednio w treści zapytań

$q = Doctrine_Query::create() ->update('Account') ->set('amount', 'amount + 200') ->where("id > {$_GET['id']}");

->where("id > ?", (int) $_GET['id']);

Page 20: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 20

Prepared statements w Propel

Podobnie jak Doctrine, oparty na PDO// poprzez Criteria$c = new Criteria();$c->add(AuthorPeer::FIRST_NAME, "Karl");$authors = AuthorPeer::doSelect($c);

// poprzez customowy SQL (czasem jest latwiej)$pdo = Propel::getConnection(BookPeer::DATABASE_NAME);$sql = "SELECT * FROM skomplikowany_sql JOIN cos_jeszcze_gorszego USING cos_tam WHERE kolumna = :col)”;$stmt = $pdo->prepare($sql);$stmt->execute(array('col' => 'Bye bye SQLi!');

Page 21: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 21

Prepared statements w Zend Framework PDO (+ mysqli + oci8 + sqlsrv)// prepare + execute$stmt = $db->prepare('INSERT INTO server (key, value) VALUES (:key,:value)');$stmt->bindParam('key', $k);$stmt->bindParam('value', $v);

foreach ($_SERVER as $k => $v) $stmt->execute();

// prepare + execute w jednym kroku$stmt = $db->query('SELECT * FROM bugs WHERE reported_by = ? AND bug_status = ?', array('goofy', 'FIXED'));

while ($row = $stmt->fetch()) echo $row['bug_description'];

Page 22: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 22

MDB2

Oparty na konkretnych sterownikach baz danych (mysql, oci8, mssql, ...)

Emuluje PS, jeśli baza ich nie wspiera$types = array('integer', 'text', 'text');$stmt = $mdb2->prepare('INSERT INTO numbersVALUES (:id, :name, :lang)', $types);

$data = array('id' => 1, 'name' => 'one', 'lang' => 'en');

$affectedRows = $stmt->execute($data);$stmt->free();

Page 23: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 23

Prepared statements - podsumowanie

Oferują bardzo dobre zabezpieczenie (jeśli użyte poprawnie)

Łatwe w użyciu, niewielkie zmiany w kodzie

Dobre wsparcie we frameworkach Mają swoje ograniczenia Czasem muszą być uzupełniane innymi

metodami zabezpieczeń

Page 24: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 24

Jak się bronić?Escape'owanie danych

Page 25: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 25

Escape'owanie – zasada działania

Dane i polecenia wciąż trzymamy w jednej zmiennej, ale zabezpieczamy je

Liczby• Rzutowanie na (int) / (float) – nie is_numeric [1]!

Teksty - zwykle otoczone apostrofami: '

• Jeśli w tekście również są apostrofy, trzeba je odróżnić od apostrofu „kończącego”

• Apostrof wewnątrz danych jest poprzedzany znakiem specjalnym, np. "\"

• Reguły escape'owania zależą od kontekstu!

.. WHERE pole = 'DANE TEKSTOWE' AND ...

Page 26: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 26

Escape'owanie – kontekst

addslashes() Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash (\) and NUL (the NULL byte). / Źródło: php.net manual /

Czy jesteś bezpieczny?

$user = addslashes($_GET['u']);$pass = addslashes($_GET['p']);

$sql = "SELECT * FROM users WHERE username = '{$user}' AND password = '{$pass}'";

$ret = exec_sql($sql);

Page 27: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP

NIE

Page 28: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 28

Escape'owanie – kontekst cd.

Różne RDBMS mają różne sposoby escape'owania danych (zależy to też od konfiguracji bazy)

addslashes() tylko „przypadkiem” działa dla MySQLRBDMS Funkcja mam 'apostrofy'

PDO $pdo->quote($val, $type) n/d (różnie)MySQL (mysql) mysql_real_escape_string mam \'apostrofy\'

MySQL (mysqli) mysqli_real_escape_string mam \'apostrofy\'

Oracle (oci8) n/d - str_replace() mam ''apostrofy''

SQLite sqlite_escape_string mam ''apostrofy''

MS SQL (mssql) n/d - str_replace() mam ''apostrofy''

PostgreSQL pg_escape_string() mam ''apostrofy''

Page 29: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 29

Escape'owanie – kontekst cd.

Nie używaj addslashes(), używaj funkcji konkretnej bazy

Czy teraz jesteś bezpieczny?

// SELECT * FROM users WHERE username = // '{$user}' AND password = '{$pass}'$_GET['u'] = "cokolwiek'"; $_GET['p'] = " or 1=1 -- ";

// MySQL widzi to tak:SELECT * FROM users WHERE username = 'cokolwiek\'' AND password = ' or 1=1 -- '

// SQLite / MS SQL / Oracle / PostgreSQL - tak:SELECT * FROM users WHERE username = 'cokolwiek\'' AND password = ' or 1=1 -- '

Page 30: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP

PRAWIE

Page 31: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 31

Pułapki escape'owania – zestawy znaków Błędy wykryte w 2006 r. w PostgreSQL i

MySQL [1] [2] W niektórych wielobajtowych zestawach

znaków pomimo escape’owania można doprowadzić do SQL injection

\ zostaje „połknięty” przez wielobajtowy znak

Przykład:• BF 27 [ ¬ ' ] BF 5C 27 [ ¬ \ '

]• Pierwsze dwa bajty to w charsecie GBK znak ¿• Serwer „zobaczy” ciąg ¿'

Page 32: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 32

Pułapki escape'owania – zestawy znaków Podatne są różne azjatyckie zestawy znaków Na szczęście nie UTF-8! W PostgreSQL zastosowano escape'owanie

poprzez '' (zamiast \') W mysql_real_escape_string() zastosowano

uwzględnianie bieżącego zestawu znaków• Nie zawsze zadziała! [1] [2]

Kontekst to również zestaw znaków

Page 33: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 33

Escape'owanie – nazwy obiektów

Nazwy kolumn, tabel, baz• Nie ma dobrej ogólnej metody na ich

escape'owanie• W różnych bazach różne listy słów

zarezerwowanych, różne długości nazw itp.

Jeśli musisz pobierać te nazwy od użytkownika, zastosuj whitelisting (blacklisting w ostateczności)

Page 34: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 34

Escape'owanie – nazwy obiektów cd.

Przykład – sortowanie po kolumnie Jest podatność w $order, ale nie możesz

użyć escape'owania$cat_id = (int) $_GET['cid'];$order = $_GET['column'];$stmt = $pdo->prepare("SELECT * FROM products WHERE cid = :cid ORDER BY $order");

$stmt->bindParam(':cid', $cat_id, PDO::PARAM_INT);

if ($stmt->execute()) { ...}

Page 35: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 35

Escape'owanie – nazwy obiektów cd.

Whitelisting

Blacklisting

$columns = array( // lista dozwolonych kolumn'product_name','cid','price',

);

if (!in_array($order, $columns, true))$order = 'product_name'; // wartosc domyslna

// tylko znaki a-z i _$order = preg_replace('/[^a-z_]/', '', $order);

// max 40 znakow$order = substr($order, 0, 40);

Page 36: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP

Escape'owanie w PDO

PDO::quote($value, $type, $len) Długość i typ bywają ignorowane!

• Liczby najlepiej rzutuj na (int), (float)• Teksty – obcinaj ręcznie

36

$quoted = $pdo->quote($input, PDO::PARAM_STR, 40);

Page 37: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 37

Escape'owanie w Doctrine

Uwaga na Doctrine'owe quote()! $q = Doctrine_Query::create();// nie tak!!! $quoted = $q->getConnection()->quote($input, 'text');

$q->update('User')->set('username', $quoted);// quote() zamienia ' na '' - exploit (MySQL):$input = 'anything\\\' where 1=1 -- ';

// trzeba escape'owac poprzez PDO - getDbh():$quoted = $q->getConnection() ->getDbh() ->quote($input, PDO::PARAM_STR);// 'anything \\\\\\\' where 1=1 -- '

Page 38: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 38

Escape'owanie w Propel

Poprzez PDO::quote()

$pdo = Propel::getConnection(UserPeer::DATABASE_NAME);

$c = new Criteria();$c->add(UserPeer::PASSWORD, "MD5(".UserPeer::PASSWORD.") "

." = " . $pdo->quote($password), Criteria::CUSTOM);

Page 39: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 39

Escape'owanie w Zend Framework

Funkcje quote(), quoteInto()

$name = $db->quote("O'Reilly");// 'O\'Reilly'

// uproszczone escape'owanie dla jednej zmiennej$sql = $db->quoteInto("SELECT * FROM products WHERE product_name = ?", 'any string');

Page 40: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 40

Escape'owanie w MDB

// funkcja quote()- trzeba określić typ$query = 'INSERT INTO table (id, itemname, saved_time) VALUES (' . $mdb2->quote($id, 'integer') .', ' . $mdb2->quote($name, 'text') .', ' . $mdb2->quote($time, 'timestamp') .')';

$res = $mdb2->exec($query);

Funkcja quote()

Page 41: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 41

Escape'owanie danych - podsumowanie

Wydaje się proste – zastępowanie tekstu Niestety, tylko się wydaje

• Musimy znać kontekst (baza danych, charset)• Istnieją błędne implementacje

Skłania do stosowania niebezpiecznych konstrukcji• sklejanie poleceń• ignorowanie zmiennych numerycznych

Stosowanie dopuszczalne tylko, jeśli• Programujemy pod konkretną bazę• Nie ma innej możliwości

Page 42: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 42

Jak się bronić?Procedury składowane

Page 43: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 43

Procedury składowane

Polecenie SQL (lub seria poleceń) zostaje przeniesione na serwer bazy danych i zapisane jako procedura

Po stronie klienta procedura zostaje wywołana z określonymi parametrami (danymi) wejściowymi i wyjściowymi

W parametrach wyjściowych klient otrzymuje wyniki procedury

Dane są formalnie oddzielone od kodu To NIE wystarcza

Page 44: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 44

Procedury składowane

Przykład w MS SQL – fragment podatnej procedury

To eval() w kolejnym wcieleniu!

CREATE PROCEDURE SP_ProductSearch@prodname varchar(400)

AS DECLARE @sql nvarchar(4000) SELECT @sql = 'SELECT ProductID, ProductName, Category, Price FROM Product Where ProductName LIKE ''' + @prodname + '''' EXEC (@sql) ...

Page 45: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 45

Procedury składowane cd.

Przykład tej samej podatności w Oracle

CREATE OR REPLACE PROCEDURE SP_ProductSearch(Prodname IN VARCHAR2) AS sqltext VARCHAR2(80);BEGIN sqltext := 'SELECT ProductID, ProductName, Category, Price FROM Product WHERE ProductName LIKE ''' || Prodname || ''''; EXECUTE IMMEDIATE sqltext; ...END;

Page 46: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 46

Procedury składowane – Dynamic SQL

Źródło podatności – Dynamic SQL• Dane znów „przemieszane” z kodem w

jednej zmiennej, która zostaje wykonana jako polecenie SQL

Jak się obronić?• Oddziel kod od danych• Escape'uj

Page 47: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 47

Procedury składowane w MS SQL

Oddzielenie danych od kodu• użyj sp_executesql razem z listą

parametrówCREATE PROCEDURE SP_ProductSearch @prodname varchar(400) = NULL ASDECLARE @sql nvarchar(4000)SELECT @sql = N'SELECT ProductID, ProductName, Category, Price FROM Product Where ProductName LIKE @p'EXEC sp_executesql @sql, N'@p varchar(400)', @prodname

Page 48: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 48

Procedury składowane w MS SQL cd.

Escape'owanie zmiennych tekstowych

Przykład:

Escape'uj tylko wtedy, kiedy musisz!(używaj sp_executesql z parametrami)

Nazwa obiektu QUOTENAME(@v)

Tekst <= 128 znaków QUOTENAME(@v,'''')

Tekst > 128 znaków REPLACE(@v,'''','''''')

SET @cmd = N'select * from authors where lname=''' +REPLACE(@lname, '''', '''''') + N''''

Page 49: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 49

Procedury składowane w Oracle

Oracle - użyj EXECUTE IMMEDIATE .. USING

Escape'owanie - pakiet DBMS_ASSERT

CREATE OR REPLACE PROCEDURE SP_ProductSearch(Prodname IN VARCHAR2) AS sqltext VARCHAR2(80);BEGIN sqltext := 'SELECT ProductID, ProductName, Category, Price WHERE ProductName=:p'; EXECUTE IMMEDIATE sqltext USING Prodname; ...END;

Page 50: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 50

Procedury składowane w MySQL

Wsparcie dla Dynamic SQL tylko poprzez prepared statements

Napisanie podatnych procedur jest trudniejsze niż procedur zabezpieczonych!

Wystarczy używać placeholderów zamiast doklejać wartości zmiennych

Page 51: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 51

Procedury składowane w MySQL cd.

PREPARE / EXECUTE USING / DEALLOCATE PREPARE

DELIMITER $$CREATE PROCEDURE get_users_like ( IN contains VARCHAR(40)) BEGIN SET @like = CONCAT("%", contains, "%"); SET @sql = "SELECT * FROM users WHERE uname LIKE ?"; PREPARE get_users_stmt from @sql; EXECUTE get_users_stmt USING @like; DEALLOCATE PREPARE get_users_stmt;END$$DELIMITER ;

Page 52: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 52

Procedury składowane w MySQL cd.

Lub jeszcze prościej (bezpośrednio)

Escape'owanie – funkcja QUOTE()

DELIMITER $$CREATE PROCEDURE get_users_like ( IN contains VARCHAR(40)) BEGIN SET @like = CONCAT("%", contains, "%"); SELECT * FROM users WHERE uname LIKE @like;END$$DELIMITER ;

Page 53: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 53

Procedury składowane w PHP

Różne wsparcie w zależności od RDBMS Wsparcie zależy od konkretnego sterownika Wspólne API (np. PDO) obsługuje tylko

najprostsze wywołania• Procedura nic nie zwraca• Procedura zwraca prosty rezultat w parametrze

OUT Różna obsługa (lub brak) bardziej

zaawansowanych wywołań• np. pobieranie rekordów z procedur, kursory

Wsparcie we frameworkach śladowe Wciąż występują błędy

Page 54: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 54

Procedury składowane w PDO

Wywołanie procedury// MySQL$sql = "CALL get_users_like(:contains)";// MS SQL – EXEC get_users_like :contains

$stmt = $pdo->prepare($sql);$ret = $stmt->execute(array('contains' => $input));

foreach($stmt->fetchAll() as $users) { var_dump($users);}

unset($s);

Page 55: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 55

Procedury składowane w Doctrine/Propel/Zend Framework Doctrine - Brak wsparcia (użyj PDO)

Propel – jw.

Zend Framework – jw.

$pdo = Doctrine_Manager::connection()->getDbh();

$pdo = Propel::getConnection(UserPeer::DATABASE_NAME);

$pdo = $db::getConnection();

Page 56: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 56

Procedury składowane w MDB2

Trzeba własnoręcznie escape'ować wszystkie parametry

$mdb2->loadModule('Function');$multi_query = $mdb2->setOption('multi_query', true);

if (!PEAR::isError($multi_query)) {

$result = $mdb2->executeStoredProc('get_users_like', array($mdb2->quote($contains, 'text')));

do { while ($row = $result->fetchRow()) {

var_dump($row); }} while ($result->nextResult());

}

Page 57: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 57

Procedury składowane - pułapki

Długość zmiennychCREATE PROCEDURE change_password

@loginname varchar(50),@old varchar(50),@new varchar(50)

AS DECLARE @command varchar(120) SET @command= 'UPDATE users SET password=' + QUOTENAME(@new, '''') + ' WHERE loginname=' + QUOTENAME(@loginname, '''') + ' AND password=' + QUOTENAME(@old, '''') EXEC (@command)GO

Page 58: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 58

Procedury składowane - podsumowanie Czasochłonne przenoszenie logiki SQL z

aplikacji na serwer Nie są łatwo przenośne pomiędzy RDBMS Napisane bezpiecznych procedur i tak wymaga

użycia prepared statements lub escape'owania danych

Źle zaimplementowane mogą zwiększyć podatność • Zarówno wywołanie procedury, jak i jej

kod jest podatny• Procedura może mieć większe

uprawnienia niż kod ją wywołujący Złe wsparcie w PHP i we frameworkach

Page 59: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 59

Procedury składowane - podsumowanieMają dużo zalet poza naszym obszarem zainteresowania Można precyzyjnie zarządzać uprawnieniami do

procedur Przydatne w wypadku stosowania różnych klientów

(Java/.NET + PHP) Mogą zwiększyć wydajność I wiele innych...Wnioski: Pozwalają osiągnąć dobre zabezpieczenie przed

SQLinjection, ale przy dużych kosztach.

Niezbędne jest zabezpieczanie samego kodu procedur przed SQL injection.

Page 60: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 60

Jak się bronić?Metody uzupełniające

Page 61: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 61

Walidacja i filtrowanie danych Kontrola poprawności danych

zewnętrznych Odbywa się przed przetwarzaniem tych

danych Nie myl z escape'owaniem!

Filter INPUT - escape OUTPUT Osobne reguły walidacji dla każdego

parametru - sprawdzaj m.in.• Typ zmiennej • Skalar / tablica• Wartości min / max• Długość danych tekstowych! [1]

Page 62: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 62

Uzupełniające metody obrony

Komplementarne do poprzednich! Zasada najmniejszych uprawnień przy łączeniu

się do bazy danych Wyłączenie nieużywanych funkcji, kont,

pakietów dostarczanych z bazą danych Regularne aktualizowanie serwera bazy danych Dobra konfiguracja PHP i bazy

• magic_quotes_* = false• display_errors = false

Dobrze zaprojektowana baza danych

Page 63: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 63

Podsumowanie

Zwracaj uwagę na SQL injection - pojedynczy błąd może wiele kosztować!

Preferuj rozwiązania kompleksowe - np. frameworki

Filtruj wszystkie dane wejściowe Pamiętaj o typach i długościach zmiennych Stosuj whitelisting zamiast blacklistingu - to

drugie kiedyś zawiedzie! Stosuj prepared statements wszędzie, gdzie

możesz Unikaj escape'owania W procedurach składowanych uważaj na

Dynamic SQL

Page 64: Kompletny przewodnik po SQL injection dla developerów PHP (i nie

OWASP 64

Linki Omawiane projekty

• sqlmap.sourceforge.net• php.net/manual/en/book.pdo.php• www.doctrine-project.org• propel.phpdb.org/trac• framework.zend.com• pear.php.net/package/MDB2

O SQL injection• www.owasp.org/index.php/SQL_Injection• unixwiz.net/techtips/sql-injection.html• delicious.com/koto/sql+injection

Hack me• threats.pl/bezpieczenstwo-aplikacji-internetowych• tinyurl.com/webgoat• mavensecurity.com/dojo.php

[email protected] http://blog.kotowicz.net