Kompletny przewodnik po SQL injection dla developerów

advertisement
Kompletny przewodnik po SQL
injection dla developerów PHP
(i nie tylko)
Krzysztof Kotowicz
PHP Developer
OWASP
10.03.2010
http://web.eskot.pl
Medycyna Praktyczna
[email protected]
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 protected]
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.
3.
4.
Przesyłamy polecenie na serwer
Podajemy zestaw danych do polecenia
Wykonujemy polecenie
5.
Odbieramy rezultat
6.
3, 4, 5 można powtarzać...
Czyścimy polecenie
PREPARE
EXECUTE
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

Zamiast SQL używa własnego języka – DQL
(emulacja dla Oracle)
i prepared statements
$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
•
•

Skłania do stosowania niebezpiecznych konstrukcji
•
•

Musimy znać kontekst (baza danych, charset)
Istnieją błędne implementacje
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
•
•

Różna obsługa (lub brak) bardziej zaawansowanych
wywołań
•


Procedura nic nie zwraca
Procedura zwraca prosty rezultat w parametrze OUT
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)) {
do {
$result = $mdb2->executeStoredProc('get_users_like',
array($mdb2->quote($contains, 'text')));
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
[email protected]
http://blog.kotowicz.net
OWASP
64
Download