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


                      Krzysztof Kotowicz
                      PHP Developer

                      http://web.eskot.pl
OWASP                 Medycyna Praktyczna
                      krzysztof@kotowicz.net
10.03.2010

                 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
                 http://www.owasp.org
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



                                            OWASP   2
Omawiane bazy danych (RDBMS)

   MySQL
   Oracle
   MS SQL Server
   W mniejszym stopniu:
     • PostgreSQL
     • SQLite




                               OWASP   3
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

                                                            OWASP          4
Co to jest SQL injection?




                            OWASP   5
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=1
  Formularzy: email=a@example.com
  Innych elementów: np. cookie, nagłówki HTTP


zostają zmanipulowane tak, że w podatnej aplikacji
zostaje wykonane „wstrzyknięte” przez
atakującego polecenie SQL.
                                            OWASP    6
Przykład – formularz logowania
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')


Użytkownik jest zalogowany bez znajomości loginu
 ani hasła
                                           OWASP    7
Dlaczego jest groźne?
      DEMO




                        OWASP   8
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



                                      OWASP    9
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


                                             OWASP    10
Jak się bronić?




                  OWASP   11
Jak się bronić przed SQL injection?

 Źródło podatności - łączenie kodu z danymi
 SELECT * FROM users WHERE login = 'login'


Metody obrony
  Oddzielenie kodu od danych
    prepared statements
    stored procedures
  Escape'owanie danych



                                             OWASP   12
Jak się bronić?
Prepared statements




                      OWASP   13
Prepared statements – zasada działania

1.   Przygotowujemy polecenie SQL (string)
     W miejsce danych wstawiamy znaczniki
                          WHERE a = ? ... WHERE a = :col

2.   Przesyłamy polecenie na serwer             PREPARE
3.   Podajemy zestaw danych do polecenia
4.   Wykonujemy polecenie                       EXECUTE
5.   Odbieramy rezultat

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

                                                OWASP      14
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
                                           OWASP    15
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();
}

                                                OWASP       16
Prepared statements - uwagi

   Nie wszystkie typy poleceń można parametryzować
   Nie w każdym miejscu polecenia można wstawić
    parametr

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


   Samo ich użycie nie wymusza stosowania parametrów
   Czasem są emulowane (ale to dobrze!)
                                              OWASP     17
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();

                                                       OWASP    18
Prepared statements w Doctrine cd.

 Wciąż można „wpaść”
   $q = Doctrine_Query::create()
       ->update('Account')
       ->set('amount', 'amount + 200')
       ->where("id > {$_GET['id']}");

 Trzeba poprawić na:

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


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

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


                                            OWASP    20
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'];
                                                 OWASP   21
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();

                                            OWASP   22
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ń




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




                       OWASP   24
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: '

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

    • 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!

                                                             OWASP   25
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 /


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

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

$ret = exec_sql($sql);


 Czy jesteś bezpieczny?

                                                                       OWASP        26
NIE

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

                                                       OWASP     28
Escape'owanie – kontekst cd.
// 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 -- '



   Nie używaj addslashes(), używaj funkcji konkretnej
    bazy
   Czy teraz jesteś bezpieczny?
                                             OWASP       29
PRAWIE

         OWASP
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 ¿'

                                               OWASP     31
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




                                            OWASP       32
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)




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

                                            OWASP      34
Escape'owanie – nazwy obiektów cd.

 Whitelisting
  $columns = array( // lista dozwolonych kolumn
  'product_name','cid','price',
  );

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

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

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

                                                  OWASP   35
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
$quoted = $pdo->quote($input, PDO::PARAM_STR, 40);




                                                     OWASP   36
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 -- '


                                                OWASP   37
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);




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




                                            OWASP     39
Escape'owanie w MDB

 Funkcja quote()

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




                                             OWASP   40
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



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




                       OWASP   42
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


                                                 OWASP       43
Procedury składowane

 Przykład w MS SQL – fragment podatnej
  procedury
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)
 ...

 To eval() w kolejnym wcieleniu!

                                           OWASP     44
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;


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




                                     OWASP     46
Procedury składowane w MS SQL

 Oddzielenie danych od kodu
 • użyj sp_executesql razem z listą
   parametrów
CREATE PROCEDURE SP_ProductSearch @prodname
varchar(400) = NULL AS
DECLARE @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


                                            OWASP   47
Procedury składowane w MS SQL cd.

 Escape'owanie zmiennych tekstowych
   Nazwa obiektu         QUOTENAME(@v)

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

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



 Przykład:
   SET @cmd = N'select * from authors where lname=''' +
   REPLACE(@lname, '''', '''''') + N''''

 Escape'uj tylko wtedy, kiedy musisz!
  (używaj sp_executesql z parametrami)
                                                   OWASP   48
Procedury składowane w Oracle

 Oracle - użyj EXECUTE IMMEDIATE ..
  USING
 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;

 Escape'owanie - pakiet DBMS_ASSERT

                                           OWASP   49
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




                                        OWASP     50
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 ;


                                             OWASP       51
Procedury składowane w MySQL cd.

 Lub jeszcze prościej (bezpośrednio)


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



 Escape'owanie – funkcja QUOTE()
                                                OWASP   52
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

                                                       OWASP   53
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);



                                            OWASP     54
Procedury składowane w
Doctrine/Propel/Zend Framework
 Doctrine - Brak wsparcia (użyj PDO)
$pdo = Doctrine_Manager::connection()->getDbh();


 Propel – jw.
$pdo = Propel::getConnection(UserPeer::DATABASE_NAME);


 Zend Framework – jw.

$pdo = $db::getConnection();



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


                                                OWASP       56
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

                                            OWASP   57
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


                                             OWASP        58
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.


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




                       OWASP   60
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]

                                          OWASP   61
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


                                             OWASP      62
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


                                                OWASP       63
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
       krzysztof@kotowicz.net       http://blog.kotowicz.net
                                                                OWASP   64

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

  • 1.
    Kompletny przewodnik poSQL injection dla developerów PHP (i nie tylko) Krzysztof Kotowicz PHP Developer http://web.eskot.pl OWASP Medycyna Praktyczna krzysztof@kotowicz.net 10.03.2010 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 http://www.owasp.org
  • 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 OWASP 2
  • 3.
    Omawiane bazy danych(RDBMS)  MySQL  Oracle  MS SQL Server  W mniejszym stopniu: • PostgreSQL • SQLite OWASP 3
  • 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 OWASP 4
  • 5.
    Co to jestSQL injection? OWASP 5
  • 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=1 Formularzy: email=a@example.com Innych elementów: np. cookie, nagłówki HTTP zostają zmanipulowane tak, że w podatnej aplikacji zostaje wykonane „wstrzyknięte” przez atakującego polecenie SQL. OWASP 6
  • 7.
    Przykład – formularzlogowania 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') Użytkownik jest zalogowany bez znajomości loginu ani hasła OWASP 7
  • 8.
  • 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 OWASP 9
  • 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 OWASP 10
  • 11.
  • 12.
    Jak się bronićprzed SQL injection?  Źródło podatności - łączenie kodu z danymi SELECT * FROM users WHERE login = 'login' Metody obrony Oddzielenie kodu od danych prepared statements stored procedures Escape'owanie danych OWASP 12
  • 13.
    Jak się bronić? Preparedstatements OWASP 13
  • 14.
    Prepared statements –zasada działania 1. Przygotowujemy polecenie SQL (string) W miejsce danych wstawiamy znaczniki WHERE a = ? ... WHERE a = :col 2. Przesyłamy polecenie na serwer PREPARE 3. Podajemy zestaw danych do polecenia 4. Wykonujemy polecenie EXECUTE 5. Odbieramy rezultat 3, 4, 5 można powtarzać... 6. Czyścimy polecenie OWASP 14
  • 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 OWASP 15
  • 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(); } OWASP 16
  • 17.
    Prepared statements -uwagi  Nie wszystkie typy poleceń można parametryzować  Nie w każdym miejscu polecenia można wstawić parametr -- 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  Samo ich użycie nie wymusza stosowania parametrów  Czasem są emulowane (ale to dobrze!) OWASP 17
  • 18.
    Prepared statements wDoctrine  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(); OWASP 18
  • 19.
    Prepared statements wDoctrine cd.  Wciąż można „wpaść” $q = Doctrine_Query::create() ->update('Account') ->set('amount', 'amount + 200') ->where("id > {$_GET['id']}");  Trzeba poprawić na: ->where("id > ?", (int) $_GET['id']);  NIGDY nie umieszczaj danych wejściowych bezpośrednio w treści zapytań OWASP 19
  • 20.
    Prepared statements wPropel  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!'); OWASP 20
  • 21.
    Prepared statements wZend 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']; OWASP 21
  • 22.
    MDB2  Oparty nakonkretnych 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(); OWASP 22
  • 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ń OWASP 23
  • 24.
  • 25.
    Escape'owanie – zasadadział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: ' .. WHERE pole = 'DANE TEKSTOWE' AND ... • 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! OWASP 25
  • 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 / $user = addslashes($_GET['u']); $pass = addslashes($_GET['p']); $sql = "SELECT * FROM users WHERE username = '{$user}' AND password = '{$pass}'"; $ret = exec_sql($sql);  Czy jesteś bezpieczny? OWASP 26
  • 27.
    NIE OWASP
  • 28.
    Escape'owanie – kontekstcd.  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'' OWASP 28
  • 29.
    Escape'owanie – kontekstcd. // 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 -- '  Nie używaj addslashes(), używaj funkcji konkretnej bazy  Czy teraz jesteś bezpieczny? OWASP 29
  • 30.
    PRAWIE 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 ¿' OWASP 31
  • 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 OWASP 32
  • 33.
    Escape'owanie – nazwyobiektó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) OWASP 33
  • 34.
    Escape'owanie – nazwyobiektó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()) { ... } OWASP 34
  • 35.
    Escape'owanie – nazwyobiektów cd.  Whitelisting $columns = array( // lista dozwolonych kolumn 'product_name','cid','price', ); if (!in_array($order, $columns, true)) $order = 'product_name'; // wartosc domyslna  Blacklisting // tylko znaki a-z i _ $order = preg_replace('/[^a-z_]/', '', $order); // max 40 znakow $order = substr($order, 0, 40); OWASP 35
  • 36.
    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 $quoted = $pdo->quote($input, PDO::PARAM_STR, 40); OWASP 36
  • 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 -- ' OWASP 37
  • 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); OWASP 38
  • 39.
    Escape'owanie w ZendFramework  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'); OWASP 39
  • 40.
    Escape'owanie w MDB Funkcja quote() // 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); OWASP 40
  • 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 OWASP 41
  • 42.
    Jak się bronić? Proceduryskładowane OWASP 42
  • 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 OWASP 43
  • 44.
    Procedury składowane  Przykładw MS SQL – fragment podatnej procedury 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) ...  To eval() w kolejnym wcieleniu! OWASP 44
  • 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; OWASP 45
  • 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 OWASP 46
  • 47.
    Procedury składowane wMS SQL  Oddzielenie danych od kodu • użyj sp_executesql razem z listą parametrów CREATE PROCEDURE SP_ProductSearch @prodname varchar(400) = NULL AS DECLARE @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 OWASP 47
  • 48.
    Procedury składowane wMS SQL cd.  Escape'owanie zmiennych tekstowych Nazwa obiektu QUOTENAME(@v) Tekst <= 128 znaków QUOTENAME(@v,'''') Tekst > 128 znaków REPLACE(@v,'''','''''')  Przykład: SET @cmd = N'select * from authors where lname=''' + REPLACE(@lname, '''', '''''') + N''''  Escape'uj tylko wtedy, kiedy musisz! (używaj sp_executesql z parametrami) OWASP 48
  • 49.
    Procedury składowane wOracle  Oracle - użyj EXECUTE IMMEDIATE .. USING 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;  Escape'owanie - pakiet DBMS_ASSERT OWASP 49
  • 50.
    Procedury składowane wMySQL  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 OWASP 50
  • 51.
    Procedury składowane wMySQL 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 ; OWASP 51
  • 52.
    Procedury składowane wMySQL cd.  Lub jeszcze prościej (bezpośrednio) DELIMITER $$ CREATE PROCEDURE get_users_like ( IN contains VARCHAR(40)) BEGIN SET @like = CONCAT("%", contains, "%"); SELECT * FROM users WHERE uname LIKE @like; END$$ DELIMITER ;  Escape'owanie – funkcja QUOTE() OWASP 52
  • 53.
    Procedury składowane wPHP  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 OWASP 53
  • 54.
    Procedury składowane wPDO  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); OWASP 54
  • 55.
    Procedury składowane w Doctrine/Propel/ZendFramework  Doctrine - Brak wsparcia (użyj PDO) $pdo = Doctrine_Manager::connection()->getDbh();  Propel – jw. $pdo = Propel::getConnection(UserPeer::DATABASE_NAME);  Zend Framework – jw. $pdo = $db::getConnection(); OWASP 55
  • 56.
    Procedury składowane wMDB2  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()); } OWASP 56
  • 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 OWASP 57
  • 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 OWASP 58
  • 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. OWASP 59
  • 60.
    Jak się bronić? Metodyuzupełniające OWASP 60
  • 61.
    Walidacja i filtrowaniedanych  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] OWASP 61
  • 62.
    Uzupełniające metody obrony Komplementarnedo 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 OWASP 62
  • 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 OWASP 63
  • 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  krzysztof@kotowicz.net http://blog.kotowicz.net OWASP 64

Editor's Notes

  • #29 Sprawdzic ansi mode
  • #51 pO tym dodac slajd, jak wywolac procedure w SQL (call / exec)