Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

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

Podobne prezentacje


Prezentacja na temat: "Copyright © The OWASP Foundation Permission is granted to copy, distribute and/or modify this document under the terms of the OWASP License. The OWASP."— Zapis prezentacji:

1 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 Kompletny przewodnik po SQL injection dla developerów PHP (i nie tylko) Krzysztof Kotowicz PHP Developer Medycyna Praktyczna

2 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

3 OWASP 3 Omawiane bazy danych (RDBMS) MySQL Oracle MS SQL Server W mniejszym stopniu: PostgreSQL SQLite

4 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 MDB Warstwa abstrakcji bazy danych (DBAL) Dystrybuowany przez PEAR

5 OWASP 5 Co to jest SQL injection?

6 OWASP 6 SQL injection – krótka definicja Jest to rodzaj ataku na aplikacje internetowe. Polega na tym, że dane od użytkownika pochodzące z: URL: Formularzy: Innych elementów: np. cookie, nagłówki HTTP zostają zmanipulowane tak, że w podatnej aplikacji zostaje wykonane wstrzyknięte przez atakującego polecenie SQL.

7 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 tak 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"; // 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}')

8 OWASP 8 Dlaczego jest groźne? DEMO

9 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

10 OWASP 10 Kilka faktów Podatności na injection na pierwszym miejscu OWASP Top RC Odpowiada za 40–60% przypadków wycieku danych [1] [2] [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

11 OWASP 11 Jak się bronić?

12 OWASP 12 Jak się bronić przed SQL injection? Źródło podatności - łączenie kodu z danymi Metody obrony Oddzielenie kodu od danych prepared statements stored procedures E scape'owanie danych SELECT * FROM users WHERE login = 'login'

13 OWASP 13 Jak się bronić? Prepared statements

14 OWASP 14 Prepared statements – zasada działania 1.Przygotowujemy polecenie SQL (string) W miejsce danych wstawiamy znaczniki 2.Przesyłamy polecenie na serwer 3.Podajemy zestaw danych do polecenia 4.Wykonujemy polecenie 5.Odbieramy rezultat 3, 4, 5 można powtarzać... 6.Czyścimy polecenie WHERE a = ?... WHERE a = :col PREPARE EXECUTE

15 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

16 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(); }

17 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!) -- blad SELECT * FROM :tabela SELECT :funkcja(:kolumna) FROM :widok -- nie tego się spodziewacie SELECT * FROM tabela WHERE :kolumna = 1 SELECT * FROM tabela GROUP BY :kolumna

18 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();

19 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']);

20 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!');

21 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'];

22 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 numbers VALUES (:id, :name, :lang)', $types); $data = array('id' => 1, 'name' => 'one', 'lang' => 'en'); $affectedRows = $stmt->execute($data); $stmt->free();

23 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ń

24 OWASP 24 Jak się bronić? Escape'owanie danych

25 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]![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...

26 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);

27 OWASP NIE

28 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 MySQL RBDMSFunkcja 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''

29 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 -- '

30 OWASP PRAWIE

31 OWASP 31 Pułapki escape'owania – zestawy znaków Błędy wykryte w 2006 r. w PostgreSQL i MySQL [1] [2] [1] [2] W niektórych wielobajtowych zestawach znaków pomimo escapeowania 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 ¿'

32 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][1][2] Kontekst to również zestaw znaków

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

34 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()) {... }

35 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);

36 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);

37 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 -- '

38 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);

39 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');

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

41 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

42 OWASP 42 Jak się bronić? Procedury składowane

43 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

44 OWASP 44 Procedury składowane Przykład w MS SQL – fragment podatnej procedury To eval() w kolejnym wcieleniu! CREATE PROCEDURE varchar(400) AS nvarchar(4000) = 'SELECT ProductID, ProductName, Category, Price FROM Product Where ProductName LIKE ''' + '''' EXEC

45 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;

46 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

47 OWASP 47 Procedury składowane w MS SQL Oddzielenie danych od kodu użyj sp_executesql razem z listą parametrów CREATE PROCEDURE varchar(400) = NULL AS nvarchar(4000) = N'SELECT ProductID, ProductName, Category, Price FROM Product Where ProductName EXEC

48 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 Tekst <= 128 znaków Tekst > 128 znaków = N'select * from authors where lname=''' + '''', '''''') + N''''

49 OWASP 49 Procedury składowane w Oracle Oracle - użyj EXECUTE IMMEDIATE.. USING Escape'owanie - pakiet DBMS_ASSERT CREATE PROCEDURE varchar(400) = NULL AS nvarchar(4000) = 'SELECT ProductID, ProductName, Category, Price FROM Product Where ProductName EXEC 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;

50 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

51 OWASP 51 Procedury składowane w MySQL cd. PREPARE / EXECUTE USING / DEALLOCATE PREPARE DELIMITER $$ CREATE PROCEDURE get_users_like ( IN contains VARCHAR(40)) BEGIN = CONCAT("%", contains, "%"); = "SELECT * FROM users WHERE uname LIKE ?"; PREPARE get_users_stmt EXECUTE get_users_stmt DEALLOCATE PREPARE get_users_stmt; END$$ DELIMITER ;

52 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 = CONCAT("%", contains, "%"); SELECT * FROM users WHERE uname END$$ DELIMITER ;

53 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

54 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);

55 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();

56 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()); }

57 OWASP 57 Procedury składowane - pułapki Długość zmiennych CREATE PROCEDURE varchar(50) AS varchar(120) 'UPDATE users SET password=' + '''') + ' WHERE loginname=' + '''') + ' AND password=' + '''') EXEC GO

58 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

59 OWASP 59 Procedury składowane - podsumowanie Mają 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 SQL injection, ale przy dużych kosztach. Niezbędne jest zabezpieczanie samego kodu procedur przed SQL injection.

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

61 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][1]

62 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

63 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

64 OWASP 64 Linki Omawiane projekty sqlmap.sourceforge.net php.net/manual/en/book.pdo.php propel.phpdb.org/trac framework.zend.com pear.php.net/package/MDB2 O 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


Pobierz ppt "Copyright © The OWASP Foundation Permission is granted to copy, distribute and/or modify this document under the terms of the OWASP License. The OWASP."

Podobne prezentacje


Reklamy Google