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

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

description

W trakcie prezentacji zademonstrujemy szkody, na jesteście narażeni nie myśląc o SQL injection. Dowiecie się, jak się przed nim bronić - zarówno w teorii, jak i na konkretnych przykładach. Nauczymy się pisać bezpiecznie w PHP 5 - sprawdzimy Zend Framework i Symfony, przenalizujemy Propel, Doctrine, PDO i mdb2. Omówimy wszystkie kruczki i różnice między różnymi systemami baz danych (Oracle, MS SQL Server, MySQL) oraz nauczymy się pisać procedury składowane odporne na SQL injection.

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

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

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

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

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

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

OWASP 5

Co to jest SQL injection?

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

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

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

OWASP 8

Dlaczego jest groźne? DEMO

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

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

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

OWASP 11

Jak się bronić?

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

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

OWASP 13

Jak się bronić?Prepared statements

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

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

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

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

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

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

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

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

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

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

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

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

OWASP 24

Jak się bronić?Escape'owanie danych

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

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

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

OWASP

NIE

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

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

RBDMS 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 tylko)

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

OWASP

PRAWIE

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

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

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

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

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

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

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

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

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

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

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

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

OWASP 42

Jak się bronić?Procedury składowane

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

OWASP 57

Procedury składowane - pułapki

Długość zmiennych

CREATE 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 tylko)

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

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

OWASP 60

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

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

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

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

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

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